Guide
Deploy a web app to a VPS with Docker Compose + HTTPS
Goal
Ship a small web app to your own VPS with Docker Compose, automatic HTTPS via Caddy, and a rollback path.
Prerequisites
- A VPS with Docker + Docker Compose installed
- A domain pointing at the VPS IP (A record)
- App source code + a
Dockerfile
Steps
- Get your app code onto the VPS: clone your repo into
/srv/myapp(or copy the files), including yourDockerfile. Ensure ports 80/443 are open on the VPS (firewall + cloud security group).git clone <your-repo-url> /srv/myapp && cd /srv/myappIf you copy files by hand instead of cloning, create the folder first:
mkdir -p /srv/myapp, then copy your code +Dockerfileinto it. - In
/srv/myapp, create acompose.yml:services: web: build: . restart: unless-stopped env_file: .env expose: ["3000"] caddy: image: caddy:2 restart: unless-stopped ports: ["80:80", "443:443"] volumes: - ./Caddyfile:/etc/caddy/Caddyfile - caddy_data:/data volumes: caddy_data: - Caddyfile (replace
app.example.comwith your domain):app.example.com { reverse_proxy web:3000 } - Create
.envon the VPS with real values — never commit it. Use placeholders in any message or log you share. - Deploy:
docker compose up -d --build docker compose logs -f --tail=100
Verify
https://your-domain(replaceapp.example.comwith your domain) loads with a valid certificate (Caddy issues it automatically).docker compose psshows both services running.- Rollback: brief downtime while containers restart — run during low traffic if possible. Steps: check out the previous commit, then
docker compose down && docker compose up -d --build.
Troubleshoot
- HTTPS not issuing: DNS A record must point at the VPS IP; ports 80/443 must be open.
- App crashes: check
docker compose logs weband audit env with env-doctor. - Port conflict: something else is on 80/443 — stop it or move Caddy.
Last updated: 2026-10-16. Edit on GitHub · Report an issue
← Previous guide · Next guide →
Still stuck? Get deployment help →