Skip to main content

Command Palette

Search for a command to run...

Remote Docker Compose Deploy on Your Own Server (Fixed-Scope Checklist)

Updated
•6 min read•View as Markdown
G
I write practical notes on DevOps and self-hosting: Docker Compose, reverse proxies, and running services on your own VPS.

A practical checklist for putting a Docker Compose stack on a VPS you already own. The server, the DNS, and the SSH keys stay with you. This is not a managed-hosting guide. It is the sequence to follow when you want a fixed-scope remote setup, and then you want the operator off the box.

What fixed scope means

Agree the outcome before anyone logs in.

  • One VPS that you already pay for, or that you will create in your own cloud account.
  • One Compose project: a named set of services, volumes, and one public hostname.
  • HTTPS on that hostname, a reverse proxy in front of the app, and a written handover.
  • After handover, the operator's access is removed. You keep the keys.

Out of scope unless you explicitly add it: rewriting the application, 24/7 on-call, or moving the workload onto someone else's platform.

1. Inventory before you touch the server

Write this down. It is the contract for the rest of the work.

Item Why it matters
VPS provider, region, size, OS You own the account. Note the public IPv4, and IPv6 if you use it.
SSH user and how login works Prefer a sudo-capable user and key auth. Turn off password login when you are done.
App and Compose file Repo URL, branch, and the compose file path. Pin image tags. Avoid floating latest in production.
Ports the app listens on Only the reverse proxy should bind 80 and 443. App ports stay on the Docker network or on localhost.
Persistent data Named volumes or bind mounts, and what a backup of them actually is.
Domain and DNS host An A or AAAA record you can edit. You keep the registrar login.
Secrets Database passwords, API keys, SMTP. They live in an env file on the server, not in git.
Who may log in List people. Temporary access should have an end date.

Also note what is already on the box: other sites, an existing Caddy, Nginx, or Traefik, the firewall (ufw or a cloud security group), and whether Docker is installed.

2. Run Compose on their VPS

The stack runs on the customer's machine. You are not renting a panel from a third party, and the VPS invoice stays in the customer's account.

  1. Create or reuse a non-root user with sudo. Add the operator's SSH public key to authorized_keys. Never share the private key.
  2. Install Docker Engine and the Compose plugin from Docker's docs for that distribution. Confirm with docker compose version.
  3. Put the project in a stable path, for example /opt/<app>. Clone the repo or copy the compose file. Keep .env out of git and chmod 600 it.
  4. Review the compose file before the first up:
    • Do not publish ports except what the proxy needs. If the app must publish a port, bind it to 127.0.0.1.
    • Set restart: unless-stopped on services that should survive a reboot.
    • Use named volumes for databases.
    • Put services that talk to each other on a user-defined network.
  5. Run docker compose pull, then docker compose up -d. Check docker compose ps and docker compose logs.
  6. Open only 22, 80, and 443 in the cloud firewall and on the host firewall. Do not leave database ports on 0.0.0.0.

3. Reverse proxy and TLS

Pick one proxy and stay with it. Caddy and Traefik are common because they can obtain certificates for you. Nginx with Certbot is fine if it is already there.

  • Point the DNS A or AAAA record at this VPS before you request a certificate.
  • The proxy is the only process bound to 80 and 443.
  • Each public hostname has a site that reverse-proxies to the Compose service, using the Docker DNS name and the internal port.
  • HTTP redirects to HTTPS.
  • Certificates renew without a person in the loop. Caddy and Traefik do this themselves. Certbot needs a timer you have actually tested.
  • Turn on HSTS only after you have confirmed HTTPS works on the real hostname.
  • Smoke test with curl -I https://your.domain. You want the app's expected status and a certificate that matches the hostname.

If the app sets cookies or redirects, pass X-Forwarded-Proto and X-Forwarded-For so it knows it is behind HTTPS.

4. Handover

Handover is a short document plus a working system, not a chat message that says "done".

Include:

  • The URL, what healthy looks like, and how to read logs (docker compose logs -f).
  • The path of the compose project and the update steps: git pull, docker compose pull, docker compose up -d.
  • Where .env and the volumes live, and one backup method (a volume snapshot, or a database dump).
  • The DNS records and which account owns them.
  • The firewall rules and the SSH user.
  • How TLS renews, and the date you last saw a valid certificate.
  • What was not set up: monitoring, off-site backups, a staging host.

Do one update and one restart while the owner is watching, so they can repeat it without you.

5. Revoke access

When the owner can operate the stack, remove the temporary access.

  1. Remove the operator's public key from ~/.ssh/authorized_keys and from any cloud SSH key list or deploy key.
  2. Rotate secrets the operator could see: database password, application secrets, registry tokens. Update .env, recreate the affected containers, and confirm the app still starts.
  3. If a temporary sudo user was created for the job, delete that user.
  4. Check recent logins (last, and the distro's auth log) and the cloud provider's console users. Close extra firewall holes that were opened for debugging.
  5. Confirm the owner still has their own key, and a break-glass path through the cloud console, before the last operator key is removed.

The customer keeps the keys. A remote setup should not leave a standing login "just in case".

Pre-flight list

  • Outcome, hostname, and repo are written down
  • VPS and DNS are in the customer's accounts
  • SSH is key-based, and the operator key is temporary
  • Compose file reviewed, and secrets are not in git
  • App is up, and only 22, 80, and 443 are public
  • HTTPS works and will renew
  • Handover notes are delivered
  • Operator access is removed and shared secrets are rotated

A fixed-price remote setup, if you want one

I do this as a bounded remote job: inventory, Compose on your VPS, reverse proxy and TLS, a written handover, then I revoke my access. You keep the server, the DNS, and the keys. There is no managed hosting in the offer.

Scope and details: https://ops.9904099.com

Email: 9904099@gmail.com