I moved the Virya workflows onto infrastructure I control. I also promised myself I would not turn this into a second full-time platform job, so the stack stayed deliberately small: Caddy, n8n and PostgreSQL in Docker on one VM.
No Kubernetes. I already have enough Kubernetes in my working hours. :)
The stack stayed boring
Caddy handles HTTPS and forwards traffic, n8n runs the workflows and PostgreSQL stores their state. The setup is pleasantly boring when everything works. The interesting part began when I tried to prove that I could recover it.
A database dump is not a backup
My first backup contained PostgreSQL but not the n8n encryption key. The credential records were there, beautifully preserved and completely useless without the key that decrypts them. The real recovery set became the database dump, encryption key, Compose files, environment values and workflow exports. A backup is not complete just because the biggest file is present.
Names matched. IDs did not.
During another migration the workflows loaded and the credentials appeared in the editor, yet several nodes insisted that the credential did not exist. The names matched. The internal IDs did not. 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 is not reachable
Then came the classic "the container is healthy" failure. n8n responded inside Docker, but the public domain did not. Caddy still had the old container address and needed a restart to refresh the path. 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 is not an end-to-end test.
Latest stopped being funny
I also stopped using latest once scheduled workflows began touching real CRM data and Gmail drafts. Updates now mean release notes, backup, pinned image, manual test workflows and only then schedules back on.
The VM is cheap. Ownership is not.
The VM itself is cheap. Ownership is the bill: updates, TLS, credentials, backups, monitoring and recovery. I accepted it because the system handles private data and business integrations, and I like knowing where it runs and how to rebuild it.
The important thing is to keep the stack serving n8n. The moment I start rebuilding a cloud platform around it, somebody should probably take the keyboard away from me.