{"id":51413959,"url":"https://github.com/kfuras/n8n-hetzner-deploy","last_synced_at":"2026-07-04T17:05:44.951Z","repository":{"id":362641887,"uuid":"1127369242","full_name":"kfuras/n8n-hetzner-deploy","owner":"kfuras","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-05T08:07:09.000Z","size":54,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-05T08:13:56.604Z","etag":null,"topics":["ansible","docker","docker-compose","hetzner-cloud","n8n","opentofu","traefik"],"latest_commit_sha":null,"homepage":"","language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kfuras.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-03T18:35:02.000Z","updated_at":"2026-06-05T06:38:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/kfuras/n8n-hetzner-deploy","commit_stats":null,"previous_names":["kfuras/n8n-hetzner-deploy"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/kfuras/n8n-hetzner-deploy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kfuras%2Fn8n-hetzner-deploy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kfuras%2Fn8n-hetzner-deploy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kfuras%2Fn8n-hetzner-deploy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kfuras%2Fn8n-hetzner-deploy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kfuras","download_url":"https://codeload.github.com/kfuras/n8n-hetzner-deploy/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kfuras%2Fn8n-hetzner-deploy/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35129344,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-04T02:00:05.987Z","response_time":113,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ansible","docker","docker-compose","hetzner-cloud","n8n","opentofu","traefik"],"created_at":"2026-07-04T17:05:44.185Z","updated_at":"2026-07-04T17:05:44.938Z","avatar_url":"https://github.com/kfuras.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# N8N Production Platform on Hetzner Cloud\n\n[![OpenTofu](https://img.shields.io/badge/OpenTofu-FFDA18?style=for-the-badge\u0026logo=opentofu\u0026logoColor=black)](https://opentofu.org/docs/)\n[![Ansible](https://img.shields.io/badge/Ansible-EE0000?style=for-the-badge\u0026logo=ansible\u0026logoColor=white)](https://docs.ansible.com/)\n[![Hetzner](https://img.shields.io/badge/Hetzner-D50C2D?style=for-the-badge\u0026logo=hetzner\u0026logoColor=white)](https://console.hetzner.com/)\n[![Docker](https://img.shields.io/badge/Docker-2496ED?style=for-the-badge\u0026logo=docker\u0026logoColor=white)](https://docs.docker.com/)\n[![N8N](https://img.shields.io/badge/n8n-EA4B71?style=for-the-badge\u0026logo=n8n\u0026logoColor=white)](https://n8n.io/)\n\nDeploy a production-ready **N8N workflow automation platform** (think Zapier or Make.com but self-hosted) with optional companion services on Hetzner Cloud. Uses OpenTofu for infrastructure provisioning and Ansible for configuration management.\n\n**Perfect for:** Automating business workflows, connecting apps, and building custom integrations without vendor lock-in.\n\n---\n\n## What You Get\n\n- **N8N** - Workflow automation with PostgreSQL database\n- **Traefik** - Automatic SSL certificates via Let's Encrypt\n- **Optional Services** - BaseRow, NocoDB, MinIO, Postiz, NCA Toolkit, Kokoro TTS\n- **Security** - Firewall, SSH hardening, fail2ban, automated secrets\n- **Ubuntu 24.04** with Docker and hardening\n\nAll secrets are automatically generated server-side using `openssl` - they never pass through OpenTofu/Ansible state.\n\n---\n\n## Architecture\n\n**Two-tool approach for production:**\n- **OpenTofu** → Creates the server (infrastructure)\n- **Ansible** → Installs \u0026 configures services (software)\n\n**Why split these?**\n- Service changes take ~30 seconds (vs 5+ minutes)\n- Safe to re-run anytime without breaking things\n- Each tool does what it's best at\n- Better day-to-day operations (logs, restarts, backups)\n\n---\n\n## Prerequisites\n\n1. **Hetzner Cloud account** + API token ([get it here](https://console.hetzner.cloud))\n   - Affordable European cloud provider (~$7/month to start)\n   - API token lets OpenTofu create servers automatically\n\n2. **OpenTofu** - Open-source infrastructure automation tool (like Terraform)\n   - **macOS:** `brew install opentofu`\n   - **Windows:** `winget install --exact --id=OpenTofu.Tofu`\n   - **Linux:** See [install guide](https://opentofu.org/docs/intro/install)\n   - Describes infrastructure as code\n\n3. **Ansible** - Configuration management tool that installs and configures software on servers\n   - **macOS:** `brew install ansible` (or `pip install ansible`)\n   - **Linux:** `sudo apt install ansible` (Ubuntu/Debian) or `pip install ansible`\n   - **Note:** Mac or Linux recommended. Ansible does not officially support Windows as a control node. See [Windows documentation](https://docs.ansible.com/projects/ansible/latest/os_guide/intro_windows.html#using-windows-as-the-control-node) for alternatives (WSL2 or remote Linux machine)\n\n4. **SSH key pair** - Secure way to access your server (no passwords needed)\n   \n   **Creating an SSH key pair:**\n   ```bash\n   ssh-keygen -t ed25519 -C \"n8n-server\" -f ~/.ssh/id_ed25519_n8n\n   ```\n   \n   This creates two files:\n   - `~/.ssh/id_ed25519_n8n` - **Private key** (keep this secret, stays on your computer)\n   - `~/.ssh/id_ed25519_n8n.pub` - **Public key** (this is what you put in terraform.tfvars)\n   \n   **Important:** Use the `.pub` file path in your `ssh_key_path` variable!\n\n5. **Your home IP address** - Get it by running: `curl ifconfig.me`\n   - Must include `/32` at the end (e.g., `123.45.67.89/32`)\n   - This restricts SSH access to only your IP for security\n\n6. **Domain name** with DNS access\n   - Any domain registrar works (Namecheap, Cloudflare, Google Domains, etc.)\n   - You'll need to add DNS A records pointing to your server\n\n---\n\n## Quick Start\n\n### 1. Infrastructure (OpenTofu)\n\n```bash\n# Clone and configure\ngit clone https://github.com/kfuras/n8n-hetzner-deploy.git\ncd n8n-hetzner-deploy/opentofu\ncp terraform.tfvars.example terraform.tfvars\ncode terraform.tfvars  # Edit with your values\n\n# Edit the required settings:\n# - server_name\n# - username\n# - hcloud_token\n# - domain\n# - home_ip\n# - ssh_key_path\n\n# Deploy\ntofu init\ntofu apply\n```\n\nThis creates the server and auto-generates `ansible/inventory.yml` and `ansible/group_vars/infrastructure.yml`.\n\n### 2. Configuration (optional)\n\n```bash\ncd ..\ncode ansible/group_vars/services.yml\n# Enable optional services: set enabled: true for nocodb, minio, etc.\n```\n\n### 3. DNS Setup\n\nAdd DNS A records pointing to your server IP (shown in tofu output).\n\n**Option 1: Wildcard (recommended)**\n```\n*.yourdomain.com → \u003cserver-ip\u003e\n```\nCovers all services automatically.\n\n**Option 2: Individual records**\n```\nn8n.yourdomain.com → \u003cserver-ip\u003e\nnocodb.yourdomain.com → \u003cserver-ip\u003e\nminio.yourdomain.com → \u003cserver-ip\u003e\nminio-data.yourdomain.com → \u003cserver-ip\u003e\n# Add records for each enabled service\n```\n\n**Tip:** DNS changes can take 5-60 minutes to propagate. You can proceed to step 4 while waiting.\n\n### 4. Deploy Services\n\n```bash\nmake install-requirements  # Installs required Ansible roles (one-time)\nmake deploy               # Configures server and starts all services (~2-3 min)\n```\n\nVisit `https://n8n.yourdomain.com` and create your admin account!\n\n**First deployment takes 2-3 minutes.** SSL certificates are generated automatically.\n\n---\n\n## Daily Operations\n\n```bash\nmake services                    # Show running containers\nmake logs service=n8n            # View last 100 lines\nmake restart                     # Restart all services\nmake restart service=traefik     # Restart one service\nmake backup                      # Backup databases\n```\n\n**To follow logs in real-time** (streaming), SSH directly to the server:\n```bash\nssh -i ~/.ssh/id_ed25519_n8n_dev USER@SERVER_IP \"cd stack \u0026\u0026 docker compose logs -f SERVICE\"\n```\n\n**Update deployment:**\n- `make deploy` - Full deployment (use when adding/removing services, changing config, or when in doubt)\n- `make update` - Quick update (only when you've changed docker-compose files in stack repo)\n\n**Enable/disable services:**\n1. Edit `ansible/group_vars/services.yml` (set `enabled: true/false`)\n2. Run `make deploy` (generates secrets for new services)\n\n---\n\n## Server Sizing\n\n**👉 Recommended for beginners:** Start with **cx23** (~$3.49/month) for n8n only. You can resize later if needed.\n\n### Cost-Optimized (CX Series) - Shared Resources Intel/AMD\nBest for development, testing, and small-scale automation:\n\n| Size | vCPU | RAM | Storage | Cost/month | Best For |\n|------|------|-----|---------|------------|----------|\n| cx23 | 2 | 4GB | 40GB SSD | ~$3.49 | **Recommended starter** - N8N core + light workflows |\n| cx33 | 4 | 8GB | 80GB SSD | ~$5.99 | N8N + 2-3 services (NocoDB, MinIO) |\n| cx43 | 8 | 16GB | 160GB SSD | ~$9.99 | Heavy automation workloads |\n| cx53 | 16 | 32GB | 320GB SSD | ~$18.99 | Large-scale production |\n\n### Regular Performance (CPX Series) - Shared AMD\nBalanced price/performance for production workloads:\n\n| Size | vCPU | RAM | Storage | Cost/month | Best For |\n|------|------|-----|---------|------------|----------|\n| cpx22 | 2 | 4GB | 80GB SSD | ~$6.99 | Budget production |\n| cpx32 | 4 | 8GB | 160GB SSD | ~$11.99 | Consistent performance |\n| cpx42 | 8 | 16GB | 320GB SSD | ~$21.99 | Business-critical automation |\n| cpx52 | 12 | 24GB | 480GB SSD | ~$31.49 | High-performance needs |\n\n**💡 My recommendation:** I run cx33 (~$5.99/month) with n8n, NocoDB, MinIO, and several workflows. Perfect sweet spot.\n\n**To change server size:**\n1. Edit `opentofu/terraform.tfvars`:\n   ```hcl\n   server_type = \"cx33\"\n   ```\n2. Run `tofu apply` (requires ~2 minute downtime)\n\nCheck current pricing at [hetzner.com/cloud](https://www.hetzner.com/cloud).\n\n---\n\n## Configuration\n\n### OpenTofu (`opentofu/terraform.tfvars`)\n\n```hcl\n# Hetzner\nhcloud_token   = \"your-hetzner-cloud-api-token\" # Change me\nssh_key_path   = \"~/.ssh/id_ed25519_your_ssh_key.pub\" # Change me\n# extra_ssh_keys = [    # Optional additional SSH keys\n#   \"ssh-ed25519 AAAA... first-key\",\n#   \"ssh-ed25519 AAAA... second-key\"\n# ]\nhome_ip      = \"your.ip.address/32\" # Change me\n\n# Server\nserver_name = \"your-server-name\" # Change me\nserver_type = \"cx23\"\nlocation    = \"hel1\"  # Helsinki (Northern Europe) | nbg1/fsn1 (Germany) | ash (US East)\nimage       = \"ubuntu-24.04\"\nusername    = \"your-username\" # Change me\n\n# GitHub\ngithub_org  = \"kfuras\"\ngithub_repo = \"n8n-production-platform\"\n\n# Domain \u0026 Core\ndomain = \"yourdomain.com\" # Change me\n\n# Security \u0026 Deployment\nenable_firewall = true\nenable_backups  = false\n```\n\n### Ansible (`ansible/group_vars/`)\n\n**infrastructure.yml** (auto-generated by OpenTofu):\n```yaml\ndomain: \"yourdomain.com\"\nhome_ip: \"1.2.3.4\"\n```\n\n**services.yml** (user-managed, never overwritten):\n```yaml\nservices:\n  baserow:\n    enabled: false\n  nocodb:\n    enabled: true   # Enable this service\n  minio:\n    enabled: true\n  # ... etc\n```\n\n---\n\n## Security\n\n- **Network:** SSH restricted to your IP, fail2ban, automatic SSL\n- **SSH:** Key-only auth, root login disabled\n- **Secrets:** Auto-generated server-side with `openssl`, never in OpenTofu/Ansible state\n- **Docker:** Non-root user, log rotation configured\n\n**⚠️ Important:** Encryption keys are permanent. Do not regenerate them post-deployment or you'll lose N8N workflow data.\n\n---\n\n## Common Mistakes\n\n**❌ Forgot to edit terraform.tfvars**\n- Copy the example file AND edit all 7 required fields marked with `# Change me`\n\n**❌ Wrong home_ip format**\n- Must include `/32` at the end: `123.45.67.89/32`\n- Get your IP: `curl ifconfig.me` (from prerequisite #6)\n\n**❌ Skipped DNS setup**\n- DNS must be configured BEFORE visiting your domain\n- Use wildcard (`*.yourdomain.com`) for easiest setup\n\n**❌ SSH key issues**\n- Make sure you use the `.pub` (public) key in terraform.tfvars\n- Private key (without .pub) stays on your computer\n\n---\n\n## Troubleshooting\n\n**DNS not propagating?**\n```bash\ndig @1.1.1.1 n8n.yourdomain.com +short  # Should show server IP\n```\n\n**Services not starting?**\n```bash\nssh -i ~/.ssh/your_key user@server-ip\ndocker ps\ndocker compose -f ~/stack/docker-compose.yml logs -f\n```\n\n**SSL certificate issues?**\n```bash\ndocker compose -f ~/stack/docker-compose.yml logs traefik | grep acme\n```\n\n**Can't SSH?**\n- Wait 2-3 minutes for server boot\n- Check `home_ip` matches your IP: `curl ifconfig.me`\n- Verify SSH key path in terraform.tfvars\n\n**Service won't enable?**\nForce re-run provisioning:\n```bash\ncd ansible \u0026\u0026 make deploy\n```\n\n---\n\n## Useful Commands\n\n```bash\n# Infrastructure\ntofu show                        # Current state\ntofu output public_ipv4          # Get server IP\ntofu destroy                     # Delete everything\n\n# SSH access\nssh -i ~/.ssh/id_ed25519_n8n user@server-ip\n\n# On server\ndocker ps                        # Running containers\ndocker compose logs -f n8n       # View logs\ndocker compose restart           # Restart all\n```\n\n---\n\n## Cost Estimate\n\n**Typical monthly costs for a production n8n setup:**\n\n| Component | Cost | Notes |\n|-----------|------|-------|\n| Server (cx23) | ~$3.49/month | 2 vCPU, 4GB RAM - perfect for n8n core |\n| Server (cx33) | ~$5.99/month | 4 vCPU, 8GB RAM - n8n + 2-3 services |\n| Traffic | $0 | 20TB included (more than enough) |\n| Backups (optional) | +20% | ~$0.70-1.20/month if enabled |\n| Domain | ~$12/year | From your registrar (not Hetzner) |\n\n**💰 Total: $3.49-5.99/month** for a fully-featured automation platform with SSL, backups, and professional infrastructure.\n\nCompare to managed alternatives:\n- n8n Cloud: $20-300/month\n- Zapier: $30-600/month\n- Make.com: $9-299/month\n\n**You own the infrastructure and data** with no workflow limits or execution quotas.\n\n---\n\n## License\n\nMIT - Use at your own risk\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkfuras%2Fn8n-hetzner-deploy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkfuras%2Fn8n-hetzner-deploy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkfuras%2Fn8n-hetzner-deploy/lists"}