Self-hosting n8n on one small VM
Caddy, n8n and PostgreSQL fit on one small VM. Most of the work went into backups, recovery, reachability checks and safe updates.
- Engineering
- DevOps
- Automation
- n8n
I moved the Virya workflows onto infrastructure I control. I also promised myself I wouldn't turn this into a second full-time platform job, so the stack stayed small: Caddy, n8n and PostgreSQL in Docker on one VM.
No Kubernetes. I already have enough Kubernetes in my working hours. :)
The stack
Caddy handles HTTPS and forwards traffic, n8n runs the workflows and PostgreSQL stores their state. It's pleasantly boring when everything works. The interesting part began when I tried to prove I could recover it.
The backup was missing the encryption key
My first backup contained PostgreSQL but not the n8n encryption key. The credential records were all there, and useless without the key that decrypts them. The recovery set is now the database dump, encryption key, Compose files, environment values and workflow exports. Having the biggest file doesn't make a backup complete.
Credential IDs after a migration
During another migration the workflows loaded and the credentials appeared in the editor, yet several nodes reported that the credential didn't exist. The names matched, but the internal IDs didn't. Imported workflows still referenced IDs from the previous database, while recreated credentials had received new ones. I repaired the mapping in PostgreSQL and added that relation to the things I export before moving anything again.
Healthy container, unreachable domain
Then came the classic "the container is healthy" failure. n8n responded inside Docker, but the public domain didn't. Caddy still had the old container address and needed a restart to pick up the new one. Since then I check the whole chain: PostgreSQL, n8n inside the Docker network, Caddy to n8n, and finally the public HTTPS endpoint. docker ps is comforting, but it isn't an end-to-end test.
Pinned images
I also stopped using latest once scheduled workflows started touching real CRM data and Gmail drafts. An update now means release notes, a backup, a pinned image, manual test workflows, and only then turning schedules back on.
The cost of ownership
The VM is cheap. The ongoing cost is ownership: updates, TLS, credentials, backups, monitoring and recovery. I accepted that because the system handles private data and business integrations, and I like knowing where it runs and how to rebuild it.
The goal is to keep the stack serving n8n. If I start rebuilding a cloud platform around it, somebody should probably take the keyboard away from me.