Production Automation Server on AWS
Self-hosting n8n on a free-tier EC2 instance, at n8n.builtbydhruv.com.
- Amazon EC2
- Docker
- Caddy
- Let's Encrypt
- Amazon Route 53
- Amazon S3
- IAM
n8n (workflow automation) running in Docker on a single free-tier EC2 instance, served over HTTPS. A Caddy container sits in front of n8n, terminating TLS and fetching/renewing a free Let's Encrypt certificate automatically. Route 53 hosts the DNS: the builtbydhruv.com apex stays on Amplify for this portfolio, and only the n8n subdomain points at this instance.
I picked a locked-down EC2 instance over the alternatives on purpose. A free ngrok tunnel gives you a temporary-looking URL and its browser-warning interstitial can break OAuth redirect callbacks. ECS/Fargate is built for stateless, scaled services — n8n is a single stateful app, and Fargate has no free tier plus needs an ALB (~$18/mo) or EFS just to make one container work. A single EC2 instance with a reverse proxy exercises the fundamentals directly: security groups, an Elastic IP, DNS, and a real TLS setup.
Architecture
n8n's port is never published to the host or opened in the security group — only Caddy, on the Docker network, can reach it.
What I did
- Launched a
t3.micro(Amazon Linux 2023), allocated an Elastic IP, and locked the security group to SSH (my IP only), HTTP, and HTTPS - Added a Route 53 A record for the
n8nsubdomain, pointed at the Elastic IP, independent of the Amplify-hosted apex - Installed Docker + Compose and added a 2 GB swap file to run both containers inside the instance's 1 GB of RAM
- Wrote the two-container Compose stack (n8n + Caddy) and a one-line Caddyfile — Caddy handles the whole reverse-proxy and TLS setup
- Split n8n's data onto its own EBS volume (bind-mounted, not the default Docker volume on the root disk) with
DeleteOnTerminationdisabled, so workflows and credentials survive the instance being replaced - Wrote a nightly cron job that tars the data volume and uploads it to S3 via an IAM instance role (no long-lived keys on the box) — a documented 24-hour RPO and ~15-minute recovery