Table of Contents 8 Sections
Overview
This guide walks through renting a small server, installing the software it needs, and running your own private n8n automation instance on it — reachable at your own domain, secured with a free HTTPS certificate that renews itself. Every technical term used along the way (VPS, SSH, Docker, reverse proxy, and so on) is explained in the Glossary section below before it's used.
Budget about 30–45 minutes for the steps below, plus extra time for DNS to update (this can take anywhere from a few minutes to about 48 hours, and is outside your control). Ongoing cost is roughly $4–15/month for the server plus $10–15/year for the domain, depending on provider. This guide has been written from technical documentation and general best practice, but — like every guide currently on Workflow Vault — it is UNVERIFIED: it has not yet been run end-to-end and confirmed working by Workflow Vault's own testing. Read each step before running it, and treat this as a well-researched starting point rather than a guarantee.
Glossary
- n8n
- n8n (pronounced "n-eight-n") is an open-source workflow automation tool. It lets you connect different apps and services together — for example, "when a new row is added to this spreadsheet, send a Slack message" — using a visual, drag-and-drop editor instead of writing a full program.
- VPS (Virtual Private Server)
- A VPS is a small computer that you rent from a hosting company. It runs continuously on the internet with its own IP address, so anything you install on it — like n8n — stays online even when your own laptop is switched off.
- SSH
- SSH (Secure Shell) is the standard way to remotely log into and control a VPS from a terminal on your own computer. You'll use an SSH command to connect to your server before you can type any commands on it.
- Docker
- Docker is a tool that packages an application together with everything it needs to run (code, system libraries, settings) into a single unit called a container. This means n8n will run the same way on your server as it does everywhere else, without you needing to manually install and configure it from scratch.
- Container
- A container is a single running instance of a packaged application (see Docker above). This guide runs two containers side by side on the same server: one for n8n, and one for Caddy.
- Docker Compose
- Docker Compose is a companion tool to Docker that lets you describe several containers — and how they connect to each other — in one plain-text file, then start (or stop) all of them together with a single command.
- Caddy
- Caddy is a web server. In this guide, its job is to sit in front of n8n, automatically obtain and renew a free HTTPS certificate, and forward incoming visitors to n8n — see "reverse proxy" below.
- Reverse proxy
- A reverse proxy is a service that receives incoming web traffic first and forwards it on to the right application behind the scenes. Here, Caddy is the reverse proxy: visitors connect to Caddy over HTTPS, and Caddy quietly forwards each request to n8n running in its own container.
- Domain
- A domain (like example.com) is the human-readable web address people type into a browser instead of a long string of numbers. You need to own one and point it at your VPS before HTTPS can work.
- DNS
- DNS (Domain Name System) is essentially the internet's phone book. Pointing your domain's "A record" at your VPS's IP address is how you tell DNS "when someone visits this domain, send them to this server." Changes can take a little while to spread across the internet.
- HTTPS
- HTTPS is the secure, encrypted version of the HTTP protocol browsers use to load websites. It requires a small digital file called a certificate. Caddy requests and renews this certificate for you automatically, at no cost, via a service called Let's Encrypt.
- Environment variable
- An environment variable is a named setting passed to a container when it starts, instead of being hard-coded into the application itself. This guide uses environment variables to tell n8n what domain it's running on.
- Port
- A port is a numbered channel a server uses to separate different kinds of network traffic — for example, web traffic conventionally arrives on ports 80 (HTTP) and 443 (HTTPS). "Opening a port" in your firewall means allowing traffic through on that specific channel.
Prerequisites
- [You'll need this already] A VPS running Ubuntu 22.04 or newer, with at least 1GB of RAM — rented from a provider such as Hetzner, DigitalOcean, Vultr, or Linode for roughly $4–6/month if you don't already have one. See the Glossary above for what a VPS is.
- [You'll need this already] A domain name you own, with its DNS A record pointed at your VPS's public IP address. Domains typically cost $10–15/year from a registrar such as Namecheap, Porkbun, or Cloudflare Registrar. Point the A record before you start, since DNS changes can take time to take effect.
- [You'll need this already] Basic comfort typing commands into a terminal and copy-pasting text. You do not need any prior experience with n8n, Docker, or Caddy specifically — every term used is explained in the Glossary above.
- [This guide installs it for you] Docker and the Docker Compose plugin. You do NOT need these installed beforehand — Step 1 below installs both on your VPS.
- [Optional, not required] An SSH key pair for logging into your VPS instead of a password. Most VPS providers let you add one when the server is created; it's a safer login method, but a password will also work for following this guide.
Where to Run This Automation
Where should you run this automation?
Don't have 24/7 hardware at home? Choose the setup that fits your budget and technical comfort.
Best for Freelancers, Agencies, and 24/7 Client Pipelines
If you don't have a dedicated server running at home, a Virtual Private Server (VPS) gives you a dedicated Linux server in the cloud that stays online 24/7 even when your personal laptop is closed.
- Cost: ~$4 to $6 / month (predictable flat fee, no per-execution surprises).
- Providers: Hetzner Cloud, DigitalOcean, Linode / Akamai, Railway, or OVH.
- Key advantage: You get a permanent public IP address and free SSL certificates, which are essential for receiving incoming Stripe, Airtable, or GitHub webhooks reliably.
curl -fsSL https://get.docker.com | sh && sudo usermod -aG docker $USER Best for Hobbyists, Testing & Zero-Cost Experimentation
You can run this automation completely free on your everyday computer (Mac, Windows, or Linux) or on low-cost home hardware like a Raspberry Pi 4/5 or repurposed mini PC.
- Cost: $0 / month (100% free).
- Software needed: Docker Desktop (free for personal use) or native Node.js / Python.
- Consideration: Automations will only execute while your computer is powered on and awake. To receive incoming webhooks locally, use tools like
ngrokorCloudflare Tunnelsfor secure local tunneling.
Best for Zero-Maintenance 1-Click Operations
If you don't want to manage Docker containers or terminal commands, you can run automations directly on managed serverless platforms or official SaaS plans:
- Cloudflare Workers: Free tier includes 100,000 requests per day with 0ms cold starts.
- n8n Cloud / Make / Zapier: Fully managed hosted platforms with automated backups and visual scenario builders.
- Railway / Render: 1-click Git container deploys with auto-healing and managed PostgreSQL databases.
Connect to your server and install Docker
First, open a terminal on your own computer and use SSH to log into your VPS, replacing YOUR_SERVER_IP with the IP address your provider gave you. Once you're connected, run the two commands below: the first downloads and runs Docker's official install script, and the second installs the Docker Compose plugin alongside it.
ssh root@YOUR_SERVER_IP
curl -fsSL https://get.docker.com | sh
sudo apt install -y docker-compose-plugin n8n and Caddy will each run inside their own Docker container. Docker itself isn't installed on a fresh VPS by default, so it has to go on first, before anything else in this guide will work.
After both commands finish, run `docker --version` and `docker compose version`. Each should print a version number (for example "Docker version 26.x.x"). If you instead see "command not found," the install did not complete — re-run the install command and check your terminal output for errors before continuing.
Create the project directory
Create a folder to hold your configuration files, plus two subfolders where n8n and Caddy will save their persistent data (workflows, credentials, and the HTTPS certificate). Keeping this data outside the containers means it survives even if a container is later recreated or updated.
mkdir -p ~/n8n-stack/{n8n_data,caddy_data}
cd ~/n8n-stack Docker containers are disposable by design — if you rebuild or update one, anything saved only inside it is lost. Mounting these folders from the server's own disk into the containers is what makes your workflows permanent.
Running `pwd` should show a path ending in /n8n-stack, and `ls` should list two empty folders: n8n_data and caddy_data.
Write the Compose file
Create a new file called docker-compose.yml in this folder (for example, run `nano docker-compose.yml` to open a simple text editor), then paste in the configuration below. It defines two services: n8n itself, and Caddy acting as the reverse proxy in front of it. Before saving, replace every instance of n8n.example.com with your own real domain name — the one you pointed at your VPS in the Prerequisites step.
services:
n8n:
image: n8nio/n8n:latest
restart: unless-stopped
environment:
- N8N_HOST=n8n.example.com
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://n8n.example.com/
volumes:
- ./n8n_data:/home/node/.n8n
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./caddy_data:/data Docker Compose reads this one file to know which containers to start, how they're configured, and how their data folders connect to the ones you created in the previous step.
After saving the file (in nano: Ctrl+O, then Enter, then Ctrl+X to exit), running `cat docker-compose.yml` should print back exactly what you pasted, with your own domain in place of n8n.example.com.
Configure Caddy
In the same folder, create a second file called Caddyfile (for example, `nano Caddyfile`) with the contents below — again replacing n8n.example.com with your actual domain. This tells Caddy which domain to serve and which internal address to forward traffic to.
n8n.example.com {
reverse_proxy n8n:5678
} Caddy needs to know two things: what domain it's issuing an HTTPS certificate for, and where to send matching traffic. `reverse_proxy n8n:5678` forwards requests to the n8n container on port 5678, which is the port n8n listens on inside the container by default.
Running `cat Caddyfile` should print your domain followed by the reverse_proxy line, with no typos in the domain name — a mismatched domain here is a common cause of certificate errors later.
Launch the stack
Start both containers in the background with the command below. On first boot, Caddy will automatically contact Let's Encrypt and request a free HTTPS certificate for your domain — this usually takes a few seconds once your DNS is correctly pointed at the server.
docker compose up -d
docker compose ps This is the step that actually brings your automation server online. Everything before this was preparation; `docker compose up -d` reads your docker-compose.yml and starts both services defined in it.
Running `docker compose ps` afterward should list both the n8n and caddy containers with a status of "Up" or "running." Then, open https://your-domain.com in a browser — you should see n8n's first-run screen asking you to create an owner account. If the page doesn't load, see the Troubleshooting section below before assuming something is broken.
Verify persistence
Log into n8n at your domain and create a simple test workflow (even a single empty node is enough), then save it. Restart the entire stack with the command below and confirm the workflow is still there afterward.
docker compose restart
docker compose ps This step exists purely to prove the setup is actually persistent — that your data lives in the n8n_data folder on the server's disk, not inside the disposable container — before you rely on it for anything real.
After the restart finishes and both containers show "Up" again in `docker compose ps`, refreshing https://your-domain.com and logging back in should show the exact same test workflow you created, unchanged.
Workflow architecture
Incoming traffic reaches Caddy first, on ports 80 and 443 (the standard ports for unencrypted and encrypted web traffic). Caddy terminates HTTPS — meaning it handles the encryption and certificate — then forwards each request over Docker's internal network to n8n, listening on port 5678 inside its own container. n8n's data (workflows, credentials, and its encryption key) lives in a folder mounted from the VPS's own disk rather than inside the container, so the container itself can be stopped, rebuilt, or updated without losing anything.
Final result
A self-hosted n8n instance reachable at your own domain over HTTPS, with a certificate that renews itself automatically, and workflow data that survives container updates and server reboots. As noted above, this guide is unverified by Workflow Vault — treat the outcome as expected-but-not-yet-confirmed until you've run it yourself.
Troubleshooting
n8n shows a 502 Bad Gateway right after deploying
Caddy started before n8n had finished booting, so the reverse proxy's target wasn't ready to accept connections yet.
Wait about 30 seconds, then restart just the Caddy service with `docker compose restart caddy` once `docker compose logs n8n` shows it's listening on port 5678.
Webhook URLs return 404 from external services
The WEBHOOK_URL environment variable in docker-compose.yml still points at the placeholder domain instead of your real one.
Edit docker-compose.yml, set WEBHOOK_URL=https://your-actual-domain.com/, then run `docker compose up -d` again to recreate the container with the new setting.
Certificate never issues, and Caddy's logs show "connection refused" on port 80
Your VPS provider's firewall (or a separate cloud security group setting) is blocking inbound traffic on port 80 or 443 before it even reaches Caddy.
Open ports 80 and 443 for inbound traffic in your provider's firewall settings, then run `docker compose restart caddy`.
The site doesn't load at all, and the browser can't find the domain
DNS hasn't finished updating yet, or the A record wasn't pointed at the correct IP address.
Double-check the A record in your domain registrar's DNS settings against your VPS's actual IP address, then wait — propagation can take anywhere from a few minutes to about 48 hours.