VOD Docs

Server setup

Building a production backend server from an empty VPS.

Flow diagram · placeholder
From empty VPS to serving traffic: OS → Docker → repo → config → DNS → stack → buckets → admin

Checklist-style flow matching the steps below.

Base system

An Ubuntu VPS with a non-root user that has sudo and SSH key login. Install Docker Engine with the Compose plugin and add the user to the docker group.

Recommended hardening: key-only SSH (no passwords, no root login) and a firewall allowing only 22, 80 and 443. Docker-published ports bypass ufw, which is one reason every internal service binds to 127.0.0.1 or publishes nothing.

Code

git clone https://github.com/Suman196pokhrel/vod-app.git ~/vod-app
cd ~/vod-app/infra

Configuration

cp .env.example prod.env
cp caddy/Caddyfile.prod.example caddy/Caddyfile.prod
  • Fill in every value in prod.env (see Secrets). Generate strong random values; never reuse the example defaults.
  • Add MAX_VIDEO_SIZE and MAX_THUMBNAIL_SIZE, which the template is missing.
  • In Caddyfile.prod, set your API domain on the site line.

DNS

Point an A record for the API domain at the server. Caddy obtains the TLS certificate automatically on first start, so DNS must resolve before the stack starts.

Start the stack

alias dc='docker compose -f docker-compose.yml --env-file prod.env'
dc up -d --build
dc exec api alembic upgrade head

Storage buckets

The backend creates the three buckets on startup. The processed and thumbnail buckets must allow anonymous reads so Caddy can serve HLS and images through /storage:

docker run --rm --network host --entrypoint sh minio/mc -c '
  mc alias set local http://127.0.0.1:9000 <MINIO_ROOT_USER> <MINIO_ROOT_PASSWORD> &&
  mc anonymous set download local/vod-processed &&
  mc anonymous set download local/vod-thumbnails'

All three commands run in one container: with separate docker run --rm calls, the alias set by the first is gone by the second. The raw-videos bucket stays private.

First admin

Sign up through the frontend, verify the email, then:

dc exec postgres psql -U vod_user -d vod_db \
  -c "UPDATE users SET role = 'ADMIN' WHERE email = 'you@example.com';"

Frontend

In the Vercel project, set NEXT_PUBLIC_API_URL=https://<api domain> and redeploy. Add the frontend's origin to CORS_ALLOW_ORIGINS in prod.env and restart the API.

To confirm on the current server

These are not recorded in the repository; check them on the live server and update this page:

ItemCommand
vCPUs, RAM, disknproc; free -h; df -h /
Firewall rulessudo ufw status verbose
Docker log rotationcat /etc/docker/daemon.json
SSH password login disabledsudo sshd -T | grep -E 'passwordauthentication|permitrootlogin'

On this page