Self-Host n8n with Docker: The Complete 2026 Guide

Self-host n8n with Docker step by step: VPS, Docker Compose, HTTPS, critical variables, backups, and the classic webhook and credential errors to avoid.

Deepyze Team··6 min read

Running n8n on your own server is the difference between paying per execution and having unlimited automation for the price of a VPS. To self-host n8n you need a VPS with 2 GB of RAM or more (USD 6-12/month), a domain pointing to the server, and Docker Compose with three pieces: the n8n container, PostgreSQL as the database, and a reverse proxy that handles HTTPS. The whole process takes 1-2 hours, and the two errors that wreck most installs —webhooks that never arrive and credentials lost on update— are prevented with two environment variables. This guide gets straight to the point and flags exactly where the traps are.

Step 0: the server and the domain

Realistic minimum requirements for light production:

  • VPS: 2 vCPU, 4 GB RAM, 40 GB of disk. Typical providers: Hetzner (~USD 6), DigitalOcean (~USD 24), Vultr (~USD 12). It works with 2 GB, but workflows with AI or heavy data will struggle.
  • OS: Ubuntu 22.04 or 24.04 LTS.
  • Domain: a subdomain like n8n.yourcompany.com with an A record pointing to the VPS's IP. Do this before you start: SSL certificate issuance needs it propagated.

Install Docker with the official script:

curl -fsSL https://get.docker.com | sh

Step 1: structure and the critical environment variables

Create the project folder and the .env file:

mkdir -p /opt/n8n && cd /opt/n8n
# .env
N8N_HOST=n8n.yourcompany.com
WEBHOOK_URL=https://n8n.yourcompany.com/
N8N_PROTOCOL=https
N8N_PORT=5678

# THE most important variable in the entire install:
N8N_ENCRYPTION_KEY=a-long-random-key-you-will-store-off-the-server

GENERIC_TIMEZONE=America/Argentina/Buenos_Aires
TZ=America/Argentina/Buenos_Aires

# Database
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=postgres
DB_POSTGRESDB_DATABASE=n8n
DB_POSTGRESDB_USER=n8n
DB_POSTGRESDB_PASSWORD=another-strong-key

# Hygiene: prune old executions so the disk doesn't blow up
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=336

Two of these variables prevent the two classic disasters:

  1. WEBHOOK_URL: without it, n8n builds webhook URLs with localhost:5678. Everything looks fine in the editor… until you connect WhatsApp, a payment provider, or any external service and the events never arrive. It's the number-one install error, and the symptom is misleading because manual executions do work.
  2. N8N_ENCRYPTION_KEY: n8n encrypts every saved credential (Google tokens, API keys, database logins) with this key. If you don't pin it, n8n generates one; if the container is recreated without the right volume or you migrate servers, the key changes and all your credentials become unreadable, with no way to recover them. Generate it with openssl rand -hex 24, put it in the .env, and keep a copy off the server (in a password manager).

Step 2: docker-compose.yml

services:
  postgres:
    image: postgres:16
    restart: unless-stopped
    environment:
      - POSTGRES_DB=n8n
      - POSTGRES_USER=n8n
      - POSTGRES_PASSWORD=${DB_POSTGRESDB_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data

  n8n:
    image: docker.n8n.io/n8nio/n8n:latest
    restart: unless-stopped
    env_file: .env
    ports:
      - "127.0.0.1:5678:5678"
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      - postgres

volumes:
  postgres_data:
  n8n_data:

One important security detail: the port is bound to 127.0.0.1, so n8n is not exposed directly to the internet. Only the reverse proxy from the next step can reach it. Bring everything up with:

docker compose up -d

Step 3: HTTPS with a reverse proxy

n8n without HTTPS is not an option: your credentials and your clients' data would travel in plain text, and most services (Meta, Google) outright reject webhooks without TLS. The simplest route is Caddy, which issues and renews certificates on its own:

apt install -y caddy
# /etc/caddy/Caddyfile
n8n.yourcompany.com {
    reverse_proxy 127.0.0.1:5678
}
systemctl reload caddy

Done: https://n8n.yourcompany.com already responds with a valid certificate. If you prefer nginx + certbot, it works just as well; just remember to enable WebSocket support (the Upgrade and Connection headers), because the n8n editor uses it and without it the interface behaves oddly.

The first time you log in, n8n asks you to create the owner user. Do it immediately: a freshly installed instance with no owner, exposed to the internet, is an open invitation.

Step 4: backups, or the insurance that costs 10 minutes

A production n8n instance accumulates two assets: the workflows and the credentials. Losing them means redoing weeks of work. The minimum viable backup is a daily cron job:

#!/bin/bash
# /opt/n8n/backup.sh
DATE=$(date +%F)
docker exec $(docker compose -f /opt/n8n/docker-compose.yml ps -q postgres) \
  pg_dump -U n8n n8n | gzip > /opt/backups/n8n-$DATE.sql.gz
cp /opt/n8n/.env /opt/backups/env-$DATE
find /opt/backups -mtime +14 -delete

And the rule that separates a real backup from wishful thinking: copy it off the server (S3, Backblaze B2, or at least another VPS). A backup that lives on the same machine dies with that machine. Remember that the .env holds the encryption key: a database dump without that key is useless.

Would you rather have someone who has done this dozens of times handle all of it? Book an intro meeting and we'll quote a turnkey install: server, HTTPS, backups, and monitoring up and running in under a week.

Common errors and their quick fixes

Symptom Likely cause Fix
Webhooks don't arrive (but the manual test works) WEBHOOK_URL missing or still set to localhost Set it to the public domain and recreate the container
"Credentials could not be decrypted" after an update or migration The N8N_ENCRYPTION_KEY changed Restore the original key from the backup; if it's lost, you must re-enter credentials by hand
The disk fills up after a few months Unlimited execution history EXECUTIONS_DATA_PRUNE=true + EXECUTIONS_DATA_MAX_AGE
The interface loads but keeps disconnecting Reverse proxy without WebSocket Enable the Upgrade/Connection headers in nginx
Slow or crashing workflows under load 1-2 GB VPS at its limit Bump to 4 GB; review workflows that load thousands of items into memory
Scheduled tasks run at random times Default timezone (UTC) Set GENERIC_TIMEZONE and TZ to your zone

To update versions: back up first, then docker compose pull && docker compose up -d. Avoid jumping from a very old version straight to the latest in one shot; n8n migrates the database schema on startup, so it's worth stepping through intermediate versions and reading the release notes.

When self-hosting is NOT worth it

This guide gives you a solid instance, but self-hosting is an ongoing responsibility, not a one-time errand:

  • If no one on your team will dedicate 2-4 hours a month (updates, logs, checking backups), n8n Cloud at USD 24/month is cheaper than your first unattended incident. We compare the total cost of both models in how much n8n costs in 2026 and in n8n cloud vs self-hosted.
  • If your workflows will handle business-critical data (collections, invoicing), the cost of a poorly secured instance isn't the server: it's the incident.
  • If you haven't validated yet that automation pays off for you, test in Cloud first and migrate later; workflows export as JSON.

Instance ready, now what?

The install is the foundation; the value is in what you build on top: connecting your CRM, automating WhatsApp, orchestrating AI agents, and plugging in your own APIs. That's where the hours you invested turn into hours saved.

And if you got this far thinking "this is exactly what I don't want to do myself": at Deepyze we install self-hosted n8n turnkey —hardened server, HTTPS, verified backups, monitoring— and we build the workflows your operation needs as part of our AI automation service. Tell us about your project and within 24 hours you'll have a fixed-price proposal, from a team in your time zone that doesn't disappear afterward: maintenance is on us too.

Frequently asked questions

What do I need to self-host n8n?+

Three things: a VPS with 2 GB of RAM or more (from USD 6-12/month), a domain or subdomain pointing to that server, and Docker with Docker Compose installed. With that, a basic install with HTTPS is up and running in 1-2 hours if you follow the right steps.

Why aren't my webhooks reaching n8n?+

This is the number-one error: the WEBHOOK_URL variable isn't set to your public domain (https://n8n.yourdomain.com/). Without it, n8n generates webhook URLs using localhost, and external services can't reach them. Also check that your reverse proxy forwards the right headers and that your firewall allows port 443.

Why did n8n lose my credentials after an update?+

Because the N8N_ENCRYPTION_KEY changed or was never pinned. n8n encrypts saved credentials with that key: if the container is recreated and generates a new key, your existing credentials become permanently unreadable. Set it explicitly in the .env from day one and store a copy off the server.

Does self-hosted n8n need PostgreSQL?+

By default it uses SQLite, which is fine for testing. For production, PostgreSQL is the better choice: it handles more volume, is more resilient to crashes, and simplifies backups. Adding it is about 15 extra lines in the docker-compose plus the DB_TYPE=postgresdb variable.

How do I update n8n without breaking anything?+

Back up first (database and the .env with the encryption key), then run docker compose pull and docker compose up -d. Avoid jumping across many versions at once: n8n migrates its data schema on startup, and it's worth reading the release notes for breaking changes in nodes you rely on.

Want this working in your company?

At Deepyze we turn manual processes into systems that work on their own: AI automation, web and mobile apps, and custom software. Tell us your case and you will have a concrete proposal within 24 hours.

Sin compromiso · Respuesta en 24 hs · Equipo en tu mismo huso horario

Keep reading