A single VPS can comfortably host several small projects, as long as they do not step on each other. These are the rules that keep a shared Docker host predictable.
Bind published ports to localhost
Docker publishes ports by writing its own firewall rules, which can bypass UFW. If a reverse proxy sits in front, expose the container only on the loopback interface:
ports:
- "127.0.0.1:8001:80"
One proxy, one shared network
Let a single reverse proxy (Nginx Proxy Manager, Traefik, Caddy) own ports 80 and 443 and terminate TLS. Attach each project's web container to an external network, declared in the compose file so it survives every up:
networks:
proxy:
external: true
If you only connect the network by hand, the next deploy recreates the container and the site answers 502.
Secrets stay out of git
Keep .env on the server, deny it in the web server, and exclude it from commits. Rotate any key that has ever been committed, even briefly.
Health checks and deploys
Expose a cheap /health/ endpoint and make the pipeline fail when it does not answer after a deploy. A deploy that "finished" but serves errors is worse than one that fails loudly.
Always create a rollback point
Before risky changes, tag the git commit, tag the running images (docker tag app:latest app:rollback-DATE) and take a database backup off the host. Going back should be a two-command operation, not an investigation.
Be careful with shared resources
Restarting Docker or upgrading its packages restarts every container on the host. Schedule such work, and never prune volumes or networks you did not create.