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#


ComponentRoleURL
CaddyReverse proxy + automatic HTTPS
dotCMSHybrid headless CMShttps://dotcms.info
PortainerDocker container management UIhttps://portainer.dotcms.info
HomarrService dashboardhttps://home.dotcms.info
DashdotVPS metricshttps://metrics.dotcms.info

Security measures in place:

  • Non-root user with sudo access only
  • SSH key-only authentication; password auth disabled
  • Root SSH login disabled
  • Firewall restricting inbound traffic to ports 22, 80, and 443

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-app to create scaffolding for your first headless project. For example:
    npx @dotcms/create-app demo

Resources#


You've completed the course!

Back to Overview
    Self-Hosting dotCMS on a VPS · Ch. 8/8 — Reviewing Your Results | dotCMS Dev Site