Tools
Tools: Latest: How I Deploy n8n to Hetzner Cloud with OpenTofu
The Problem with Manual Server Setup
The Solution: A Three-Command Deployment
System Architecture
Breaking Down the Components
1. OpenTofu as My Infrastructure Manager
2. Security Hardening (Automatic)
3. SSL Certificates (Zero Configuration)
4. Service Configuration
5. DNS Verification
The Deployment Experience
Why This Works Better Than Other Solutions
Prerequisites
Getting Started (The DIY Path)
Day-to-Day Operations
Final Thoughts
Get the Complete Implementation
Related Posts I wanted to deploy n8n to Hetzner Cloud with OpenTofu with proper security, SSL certificates, and infrastructure-as-code—because the manual process was eating hours of my time. Spinning up a new self-hosted n8n instance used to take me about an hour of clicking through interfaces and running commands. Now I do it in under 5 minutes with 3 OpenTofu commands. Here’s how I built this system. Docker Compose handles the application stack—but before you can run docker compose up, you need a server. Before automation, that meant: That’s about an hour of setup before Docker Compose even runs. Miss one security setting and you’re vulnerable. Forget to generate a strong password and you’ve got a weak point. I found myself putting off new deployments because the overhead just wasn’t worth it. Total: 3 commands, ~5 minutes of automation time. Everything else—server provisioning, Docker installation, security hardening, SSL certificates, secret generation, container deployment—happens automatically. I still configure my domain’s DNS records manually, but even that takes 30 seconds. The system handles multiple services too. Want to add BaseRow, NocoDB, or MinIO alongside n8n? Flip a toggle in your config file and redeploy. No manual intervention needed. Want the complete OpenTofu configuration and Docker stack? I’m sharing everything—templates, scripts—in my Build-Automate community. More on that at the end. Here’s how the pieces fit together: Infrastructure Layer: Hetzner Cloud The foundation is Hetzner’s affordable cloud platform: Configuration Layer: OpenTofu OpenTofu (the open-source Terraform fork) manages everything: Service Layer: Docker Compose The actual applications run in containers: I use OpenTofu because it’s the open-source fork of Terraform with no licensing concerns. The configuration is declarative—I describe what I want, and OpenTofu figures out how to make it happen. My project has 6 key files that work together: The architecture follows a cloud-init + provisioner model. When you run tofu apply: The tricky part was getting the timing right. Cloud-init runs asynchronously, so the provisioners need to wait for it to complete before doing anything. The script polls until Docker is running and the repo is cloned. The service toggle system is what makes this really flexible. In your config file, you define which services you want: Run tofu apply and only the enabled services deploy. Change a toggle and re-run—the system adds or removes services without touching the others. The actual implementation involves some clever sed manipulation and Docker Compose includes that I’ll share in the full walkthrough below. Most tutorials skip security or add it as an afterthought. This system includes it by default. The secret management approach took some iteration to get right. The naive approach—generating secrets in Terraform—means they end up in your state file. Not ideal. I use a provisioner-based approach that generates secrets on the server itself, so they never touch your local machine. More on this in the Service Configuration section below. The exact sshd_config settings and fail2ban configuration are in the complete implementation. Traefik handles SSL automatically: When a new service starts, Traefik: No certbot cron jobs. No manual renewal. Services are controlled through Docker Compose with a modular include system: The configure script reads your service settings (passed as JSON) and toggles each service: The sed patterns handle commenting/uncommenting the include lines. The tricky part was matching both states (commented and uncommented) without breaking YAML syntax. For secrets, each service defines which environment variables it needs. The script generates them all upfront: The tr -d '/+=' strips special characters that break some databases. Secrets are generated on the server, never stored in Terraform state. The full script also handles .env file creation, preserving existing secrets on re-runs, and copying service-specific environment files. Here’s a problem that bit me early on: Traefik tries to get SSL certificates immediately when containers start. If DNS hasn’t propagated yet, Let’s Encrypt validation fails. Worse, you get rate-limited and have to wait an hour before trying again. The solution is a DNS verification script that runs before starting any containers: The script loops through all enabled services and checks each subdomain against Cloudflare’s DNS (1.1.1.1). It waits up to 30 minutes for DNS to propagate before starting containers. This prevents the frustrating “why isn’t my SSL working” debugging session that wastes hours. The full script includes status logging so you can see which domains are still pending. Now when I want to spin up a new n8n instance: Need to add a service later? Even easier: Need to destroy everything? One command removes the server, firewall, and SSH key from Hetzner. Nothing left behind. vs. Managed n8n Cloud: vs. Docker on a Random VPS: Before you start, you’ll need: If you want to build this yourself using the concepts above: The concepts in this article give you the roadmap. The implementation details—especially the timing and sequencing—are where most people get stuck. Once deployed, managing your stack is straightforward Docker Compose: The infrastructure-as-code approach means you can always tofu destroy and tofu apply to get a fresh start. Your workflows are stored in n8n’s PostgreSQL database, which you can back up separately. I test every deployment step before publishing. Writing about infrastructure automation means the configurations have to actually work. Building this from scratch took me about two weeks of iterations—figuring out the right cloud-init sequence, debugging provisioner timing, getting the DNS wait logic right, handling edge cases in the service toggle system. If you use my templates, you’ll skip all that frustration. If you deploy n8n regularly or want a professional infrastructure setup without the manual overhead, this approach might work for you too. This article covers the architecture and key concepts. But there’s a gap between understanding the approach and having working infrastructure. What you need to actually deploy: I’m sharing the complete implementation in my Build-Automate community ($24/month): ✅ Complete OpenTofu project (all 6 files, tested and documented)
✅ Extended Docker Compose stack with 10+ services pre-configured✅ Private GitHub org access (same infrastructure I run in production)✅ Troubleshooting guides for common issues
✅ Direct support when you get stuck Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse