Self-Hosting dotCMS on a VPS: A Complete Production Guide
Reviewing Your Results
Final Caddyfile#
Your complete /etc/caddy/Caddyfile should look like this:
dotcms.info { reverse_proxy localhost:8082 } portainer.dotcms.info { reverse_proxy localhost:9000 } home.dotcms.info { reverse_proxy localhost:7575 } metrics.dotcms.info { reverse_proxy localhost:3001 }
All HTTPS certificates are handled automatically by Caddy. No certificate renewal jobs, no ACME configuration.
What You've Built#
| Component | Role | URL |
|---|---|---|
| Caddy | Reverse proxy + automatic HTTPS | — |
| dotCMS | Hybrid headless CMS | https://dotcms.info |
| Portainer | Docker container management UI | https://portainer.dotcms.info |
| Homarr | Service dashboard | https://home.dotcms.info |
| Dashdot | VPS metrics | https://metrics.dotcms.info |
Security measures in place:
- Non-root user with
sudoaccess only - SSH key-only authentication; password auth disabled
- Root SSH login disabled
- Firewall restricting inbound traffic to ports
22,80, and443
Troubleshooting#
Caddy isn't issuing a certificate#
DNS must fully propagate before Caddy can get a Let's Encrypt certificate. Run ping dotcms.info and confirm it resolves to your static IP. If DNS is correct, restart Caddy:
sudo systemctl restart caddy
dotCMS admin panel not loading#
Check container logs in Portainer. dotCMS takes 60–90 seconds to fully initialize on first boot. The OpenSearch and PostgreSQL containers must be healthy first. If PostgreSQL is still starting, dotCMS will wait.
Can't SSH after changing firewall rules#
If you accidentally block port 22, use your cloud provider's console (web-based terminal access) to re-add the SSH rule. DigitalOcean: Droplet → Access → Launch Console.
Docker commands require sudo#
Your user needs to be in the docker group. Run sudo usermod -aG docker YOUR_USER, then log out and back in.
Next Steps#
- Connect a frontend: dotCMS exposes both REST and GraphQL APIs. Build a Next.js or React frontend pointed at
https://dotcms.info/api/v1/. - Add more services: Any open-source tool with a Docker image can be added to this stack — just add a Portainer stack and a Caddyfile block.
- Updates: To update dotCMS, pull the new image in Portainer (Stacks → dotcms → Editor → pull and redeploy) or via
docker compose pull && docker compose up -d. - Backups: Consider adding pgBackWeb as an additional Portainer stack for scheduled PostgreSQL backups with a web UI.
- Integrations by CLI: To start building with dotCMS as a headless CMS, use
@dotcms/create-appto create scaffolding for your first headless project. For example:npx @dotcms/create-app demo
Resources#
- dotCMS Documentation
- dotCMS Community
- dotCMS Releases
- Caddy Documentation
- Portainer Documentation
- Homarr Dashboard
- Dashdot
You've completed the course!
Back to Overview