Skip to content

Deployment Guide

Requirements

1. Clone the Repository

git clone https://github.com/diablofong/ShotCut.git
cd ShotCut

2. Configure Environment Variables

cp .env.example .env

Open .env and update the following required values:

# Generate a strong secret key (minimum 32 characters)
SECRET_KEY=<run: python -c "import secrets; print(secrets.token_urlsafe(32))">

# Database credentials (use strong passwords)
MYSQL_ROOT_PASSWORD=your-strong-root-password
MYSQL_PASSWORD=your-strong-db-password

# Admin account
ADMIN_PASSWORD=your-admin-password

# Database connection
DATABASE_URL=mysql+aiomysql://shotcut:<MYSQL_PASSWORD>@db:3306/shotcut

3. Start Services

docker compose up -d

Open http://localhost:8000 in your browser.


Local Development

Backend:

python -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate

pip install -r backend/requirements.txt

cp .env.example .env
# Edit DATABASE_URL to point to your MariaDB instance

alembic upgrade head

uvicorn backend.main:app --reload

Frontend:

cd frontend
npm install
npm run dev

Frontend dev server: http://localhost:5173 — Backend API: http://localhost:8000


Production Checklist

Before going live

These steps are required for a secure production deployment.

Environment Variables

Variable Requirement
SECRET_KEY ≥32 characters, randomly generated
MYSQL_ROOT_PASSWORD ≥16 characters, mixed case + symbols
MYSQL_PASSWORD ≥16 characters, mixed case + symbols
ADMIN_PASSWORD Change from default admin1234
CORS_ORIGINS Set to your production domain only

HTTPS Setup

Deploy behind a reverse proxy (nginx or Caddy) with valid SSL/TLS:

server {
    listen 443 ssl;
    server_name shotcut.example.com;

    location / {
        proxy_pass http://localhost:8000;
        proxy_set_header X-Forwarded-Proto https;
    }
}

Tip

Setting X-Forwarded-Proto: https automatically enables the Cookie Secure Flag. You do not need to set IS_PRODUCTION=true manually.

CORS Configuration

CORS_ORIGINS=https://shotcut.example.com

Container Security

  • Application runs as non-root user (shotcut) inside Docker containers
  • Database port (3306) is not exposed to the host — accessible only via internal Docker network
  • Sensitive files (.env, .git, data/) are excluded from Docker images via .dockerignore

Token Security

  • Access Tokens stored in memory (not localStorage) — automatically cleared on page close
  • Refresh Tokens stored in httpOnly cookies — immune to XSS attacks
  • No tokens in URL query parameters — prevents leakage in logs and browser history

File Upload Security

  • Magic number validation prevents fake file extensions (e.g., .exe renamed to .mp4)
  • Filename sanitization removes path traversal characters (../, ..\\)
  • FFmpeg command injection prevention via path validation and shell escaping

Security Monitoring

After deployment:

  • Monitor application logs for authentication failures and suspicious activity
  • Keep dependencies updated: docker compose build --pull periodically
  • Subscribe to security advisories for FastAPI, React, and MariaDB

Running Tests

Tests use SQLite in-memory — no MariaDB instance required:

# Run inside Docker (recommended — no local Python needed)
docker run --rm \
  --entrypoint python \
  -e DATABASE_URL="sqlite+aiosqlite:///:memory:" \
  -e SECRET_KEY="test-secret-key" \
  shotcut-app \
  -m pytest backend/tests/ -v --cov=backend

Current status: 23/23 tests passing — 56% coverage (threshold: 50%)


Upgrading from Earlier Versions

Breaking Change: If you're upgrading from a version prior to the security fixes (commit e371ae7):

  • All users must re-login due to token storage mechanism changes (localStorage → memory)
  • Refresh Tokens are now httpOnly cookies — update any custom API clients

Environment Variable Reference

Variable Required Default Description
DATABASE_URL MariaDB connection string
SECRET_KEY JWT signing key (≥32 chars)
IS_PRODUCTION false Force Cookie Secure Flag
ADMIN_USERNAME admin Initial admin username
ADMIN_PASSWORD admin1234 Initial admin password (change this)
JWT_ACCESS_EXPIRE_MINUTES 15 Access token lifetime (minutes)
JWT_REFRESH_EXPIRE_DAYS 7 Refresh token lifetime (days)
CORS_ORIGINS "" Allowed CORS origins (comma-separated)
MAX_UPLOAD_SIZE_MB 2048 Upload size limit (MB)
FFMPEG_TIMEOUT 300 FFmpeg processing timeout (seconds)
APP_PORT 8000 Exposed port

Useful Commands

# View logs
docker compose logs -f app

# Stop services
docker compose down

# Rebuild after updates
docker compose build --pull && docker compose up -d

# Run tests
docker run --rm \
  --entrypoint python \
  -e DATABASE_URL="sqlite+aiosqlite:///:memory:" \
  -e SECRET_KEY="test-secret-key" \
  shotcut-app \
  -m pytest backend/tests/ -v