Deployment Guide¶
Requirements¶
- Docker and Docker Compose
Docker Deployment (Recommended)¶
1. Clone the Repository¶
2. Configure Environment Variables¶
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¶
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:
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¶
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.,
.exerenamed 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 --pullperiodically - 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