Remote Docker Compose Deploy on Your Own Server (Fixed-Scope Checklist)
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.
- Create or reuse a non-root user with sudo. Add the operator's SSH public key to
authorized_keys. Never share the private key. - Install Docker Engine and the Compose plugin from Docker's docs for that distribution. Confirm with
docker compose version. - Put the project in a stable path, for example
/opt/<app>. Clone the repo or copy the compose file. Keep.envout of git andchmod 600it. - 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-stoppedon services that should survive a reboot. - Use named volumes for databases.
- Put services that talk to each other on a user-defined network.
- Do not publish ports except what the proxy needs. If the app must publish a port, bind it to
- Run
docker compose pull, thendocker compose up -d. Checkdocker compose psanddocker compose logs. - 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
.envand 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.
- Remove the operator's public key from
~/.ssh/authorized_keysand from any cloud SSH key list or deploy key. - Rotate secrets the operator could see: database password, application secrets, registry tokens. Update
.env, recreate the affected containers, and confirm the app still starts. - If a temporary sudo user was created for the job, delete that user.
- 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. - 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
