Self-Hosting Guide
How to Self-Host SupoClip with Docker Compose
A practical walkthrough of the repository's Docker Compose stack: what each service does, which keys you need, how to run without cloud AI, and what to harden before production.
By SupoClip · Updated · 9 min read
SupoClip is the open-source AI video clipper behind the hosted app at supoclip.com. The same repository ships a Docker Compose stack you can run on a laptop, a home server, or a VPS. This guide follows that stack exactly as it exists in the repository. Where a step depends on your provider or hardware, we say so instead of guessing.
Prefer not to run infrastructure? The hosted app runs the same pipeline. Still deciding between tools? Read our SupoClip vs OpusClip comparison first.
What you are running
docker compose up starts these containers, all defined in docker-compose.yml:
| Service | Role | Host port |
|---|---|---|
| frontend | Next.js web app, auth, and API proxy | FRONTEND_PORT (3107 in .env.example; 3001 if unset) |
| backend | FastAPI API; docs at /docs | 8000 |
| worker | ARQ worker: download, transcribe, analyze, render | none |
| postgres | PostgreSQL 15: users, tasks, clips | none |
| redis | Redis 7: job queue and live progress | 6379 |
| mcp | MCP server for AI clients (optional to keep running) | 9100 |
Every published port is bound to 127.0.0.1, so a fresh install is reachable only from the machine itself. Generated clips, uploads, Postgres, and Redis are stored in named Docker volumes, so they survive container restarts. If you don't use MCP, stop that container with docker compose stop mcp.
Requirements
- Docker with Compose v2 (
docker compose) or the standalonedocker-compose, plus Git. - A transcription source, set with
TRANSCRIPTION_PROVIDER:assemblyai(default, needsASSEMBLY_AI_API_KEY),whisper(local, no key), oryoutube_captions(YouTube links only). - An LLM for clip selection, set with
LLM=provider:model: OpenAI, Google, Anthropic, OpenRouter (each needs its API key), or a localollama:model. - Optional:
PEXELS_API_KEYfor B-roll overlays.
The first image build downloads Python and Node dependencies. The quick start estimates 5–10 minutes; it varies with your connection and CPU.
Install with Docker Compose
1. Clone and create your environment file
git clone https://github.com/FujiwaraChoki/supoclip.git
cd supoclip
cp .env.example .env2. Pick providers in .env
The template defaults to LLM=openrouter:anthropic/claude-sonnet-5.5. Processing fails unless you set OPENROUTER_API_KEY or point LLM at a provider you have a key for. A minimal cloud setup looks like this:
TRANSCRIPTION_PROVIDER=assemblyai
ASSEMBLY_AI_API_KEY=your_assemblyai_key
LLM=google-gla:gemini-3-flash-preview
GOOGLE_API_KEY=your_google_key
# Replace every placeholder secret, even for local use
BETTER_AUTH_SECRET=generate_with_openssl_rand_base64_32
BACKEND_AUTH_SECRET=generate_another_one
APP_SETTINGS_ENCRYPTION_KEY=and_another_oneGenerate each secret with openssl rand -base64 32. SELF_HOST=true (the default) keeps billing off and removes per-account generation limits.
3. Build and start
docker compose up -d --build
# or: ./start.sh (checks .env and Docker first, then does the same)Open http://localhost:3107, create an account, and paste a YouTube link or upload a file. API docs are at http://localhost:8000/docs.
Option: fully local transcription and clip selection
To keep transcripts and analysis on your own hardware, combine local Whisper with an Ollama model:
TRANSCRIPTION_PROVIDER=whisper
WHISPER_MODEL_SIZE=medium # tiny | base | small | medium | large | large-v3
LLM=ollama:gpt-oss:20b
# Empty = http://host.docker.internal:11434/v1 inside the containers.
# If you set it yourself, keep the /v1 suffix.
OLLAMA_BASE_URL=The worker makes the Ollama calls from inside its container, so Ollama must be reachable from there, not just from your shell. How you get there depends on where Ollama runs:
Ollama on the host, with Docker Desktop (macOS, Windows)
No extra setup. Docker Desktop forwards host.docker.internal to the host, so leave OLLAMA_BASE_URL empty and pull the model with ollama pull gpt-oss:20b.
Ollama on the host, with Docker Engine (Linux)
Compose maps host.docker.internal to the Docker bridge gateway, but Ollama listens only on 127.0.0.1 by default, so the worker's connection is refused. Make Ollama listen on all interfaces:
sudo systemctl edit ollama
# [Service]
# Environment="OLLAMA_HOST=0.0.0.0:11434"
sudo systemctl restart ollama
ollama pull gpt-oss:20bThat also exposes port 11434 on your network interfaces. Ollama has no authentication, so block that port from outside the machine with your firewall. If your firewall filters traffic from Docker networks, also allow the containers to reach it.
Ollama in its own container
Add it to the stack with an override file, then point SupoClip at the service name instead of the host:
services:
ollama:
image: ollama/ollama
volumes:
- ollama_models:/root/.ollama
restart: unless-stopped
volumes:
ollama_models:OLLAMA_BASE_URL=http://ollama:11434/v1
docker compose up -d ollama
docker compose exec ollama ollama pull gpt-oss:20b
docker compose up -d backend worker # pick up the new .env valueWhichever setup you choose, test from inside the worker before submitting a video. The check looks for gpt-oss:20b because that is the model in the example LLM value. If you chose another model, replace it with the exact name after ollama: in your LLM setting. Ollama lists a model pulled without a tag as name:latest, so use that form in the check.
# Host Ollama:
docker compose exec worker curl -s http://host.docker.internal:11434/api/tags | grep -q '"gpt-oss:20b"' && echo "model ready"
# Ollama container:
docker compose exec worker curl -s http://ollama:11434/api/tags | grep -q '"gpt-oss:20b"' && echo "model ready"model ready means the worker can reach Ollama and the model you searched for is installed. No output means either the connection failed or the model is missing, so rerun the command without the grep to see which. Smaller Whisper models trade accuracy for speed, and clip quality depends on the LLM. Compare a local model against a hosted one on a recording you know before switching your whole backlog.
Verify the install
docker compose ps # every service should be "healthy"
curl -f http://localhost:8000/health/db # backend can reach Postgres
docker compose logs -f worker # watch a job move through the pipeline- Create an account and submit a short video first, so a misconfiguration fails fast.
- Confirm the task page shows progress updates. If it stays queued, check the worker logs.
- Play and download a finished clip.
Stuck tasks are usually a missing provider key or a worker that cannot reach Redis. The troubleshooting guide covers the common cases.
Production checklist
The Compose file is tuned for getting started. Before putting it on a public domain:
- Build the production frontend. Compose defaults to the hot-reloading
developmenttarget and mountsfrontend/srcinto the container. SetFRONTEND_BUILD_TARGET=runnerandNODE_ENV=productionin.env, then drop the source mounts with the override below. - Terminate HTTPS at a reverse proxy (Caddy, nginx, Traefik) in front of the frontend port.
- Expose the backend for uploads. With
BACKEND_AUTH_SECRETset, browsers upload video files directly toNEXT_PUBLIC_API_URL. Point that at a public HTTPS backend URL, and add your frontend origin toCORS_ORIGINS. - Set your public origin everywhere:
NEXT_PUBLIC_APP_URL,BETTER_AUTH_URL, andCORS_ORIGINS.NEXT_PUBLIC_*values are baked in at build time, so rebuild the frontend after changing them. - Change the database password.
docker-compose.ymlhardcodessupoclip_passwordin four places:POSTGRES_PASSWORDin thepostgresservice andDATABASE_URLin thefrontend,backend, andworkerservices. EditingPOSTGRES_PASSWORDin.envalone changes none of them. Override all four, as shown below. - Set
REDIS_PASSWORD; the Redis container enables AUTH when it is non-empty. - Close sign-ups with
DISABLE_SIGN_UP=trueif the instance is just for your team. - Review the limits:
MAX_VIDEO_DURATION(default 5400 s, 90 minutes) andMAX_VIDEO_UPLOAD_BYTES(about 12 GB).
services:
frontend:
volumes: !reset [] # the runner image already contains the buildFor the database password, put a new value in POSTGRES_PASSWORD in .env. Generate it with openssl rand -hex 24, because hex needs no escaping inside a URL. Then point all four settings at it in the same override file:
services:
postgres:
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
frontend:
environment:
DATABASE_URL: postgresql://supoclip:${POSTGRES_PASSWORD}@postgres:5432/supoclip
backend:
environment:
DATABASE_URL: postgresql+asyncpg://supoclip:${POSTGRES_PASSWORD}@postgres:5432/supoclip
worker:
environment:
DATABASE_URL: postgresql+asyncpg://supoclip:${POSTGRES_PASSWORD}@postgres:5432/supoclipPostgres reads POSTGRES_PASSWORD only when it initializes an empty data volume. On an install that already has a database, changing that variable does not change the actual password. Rotate it inside Postgres first, using the same value as in .env. Then recreate the services so they use the new URLs:
docker compose exec -T postgres psql -U supoclip -d supoclip \
-c "ALTER USER supoclip PASSWORD 'your_new_password'"
docker compose up -ddocs/configuration.md lists every variable, and docs/setup.md covers custom ports and public URLs.
Upgrades and backups
init.sql creates the schema only when the Postgres volume is first initialized. After that, nothing updates the schema automatically. If a new version adds files under frontend/prisma/migrations or backend/migrations, apply them yourself before you restart the app. New code running against the old schema fails on missing columns and tables. Back up first, and stop the app services so they don't pick up new code early. The development frontend hot-reloads mounted source.
docker compose exec -T postgres pg_dump -U supoclip supoclip > supoclip-$(date +%F).sql
docker compose stop frontend backend worker
old=$(git rev-parse HEAD)
git pull
git diff --name-only --diff-filter=A "$old" HEAD -- \
'backend/migrations/*.sql' 'frontend/prisma/migrations/*/migration.sql'Read each listed file, then apply it in a single transaction. Apply Prisma migrations in timestamp order and backend migrations in number order:
docker compose exec -T postgres psql -U supoclip -d supoclip -v ON_ERROR_STOP=1 -1 \
< frontend/prisma/migrations/<new_migration>/migration.sql
docker compose up -d --buildA database created by init.sql has no Prisma migration history. Don't run prisma migrate deploy against it: Prisma would replay every migration from the first one and fail on tables that already exist. If you want Prisma to manage migrations from now on, first baseline it with prisma migrate resolve --applied <migration>, but only for migrations whose changes are already in your schema. Prisma permanently skips any migration you mark as applied, even if its changes are missing.
Back up the clips and uploads volumes too if you need to keep rendered videos. Never run docker compose down -v on an instance you care about: it deletes every volume, including the database.
Want to customize the output? Drop .ttf fonts into backend/fonts/ and .mp4 transitions into backend/transitions/. They appear in the app automatically. For automation, create an API key in Settings and use the REST API or the bundled MCP server.
Frequently asked questions
Is self-hosted SupoClip free?
The code is free under AGPL-3.0 and self-host mode has no generation limits or paywall. You still pay for your own hardware or server, and for any cloud transcription or LLM usage you configure. A Whisper + Ollama setup avoids per-request AI fees but needs enough local compute.
Can SupoClip run without any cloud AI services?
Yes, for clip analysis. Set TRANSCRIPTION_PROVIDER=whisper to transcribe locally and LLM=ollama:<model> to select clips with a local model. Downloading YouTube links still needs internet access; uploaded files do not. Optional B-roll uses the Pexels API.
Do I need a GPU?
No. The .env.example template sets BACKEND_CPU_ONLY=true, which installs CPU-only PyTorch. CPU-only Whisper transcription is slower than a cloud API, so long videos take longer on modest hardware.
Does it work on Apple Silicon?
Yes, with a caveat: the backend and worker images run as linux/amd64 under emulation. BACKEND_CPU_ONLY=true avoids NVIDIA downloads, but it does not enable Apple GPU acceleration.
How do I keep my instance private?
Create your account, then set DISABLE_SIGN_UP=true and recreate the frontend container. Compose binds every port to 127.0.0.1, so nothing is public until you add a reverse proxy.
Try it on one recording you know well.
Use the hosted app, or run the same open-source code on your own machine.
Keep reading
Open Source
Open-source video clipper you can control
What self-hosting changes about control, model providers, and infrastructure.
ReadComparison
SupoClip vs OpusClip: a sourced comparison
Pricing model, deployment, watermarks, and workflow, with every claim linked to a source.
ReadYouTube to Shorts
Repurpose long YouTube videos into Shorts
A practical workflow for selecting, captioning, reframing, and reviewing clips.
Read