← Back to blog
How to Install n8n as a Service: Local Docker, VPS with Nginx, and Coolify (Beginner Developer Guide)
n8ndockerdocker-composevpsnginxreverse-proxysslletsencryptcoolifydeploymentautomationwebhookstraefik

How to Install n8n as a Service: Local Docker, VPS with Nginx, and Coolify (Beginner Developer Guide)

By Imran Khan·Mar 20, 2026·25m read

A deep, step-by-step guide to running n8n as a reliable service locally with Docker, on a VPS with Docker + manual Nginx reverse proxy, and deploying via Coolify—plus requirements, examples, and official documentation links.

n8n is easiest to try in minutes, but it’s also the kind of tool you quickly want to run as a service: stable restarts, persistent data, a real domain, HTTPS, and predictable upgrades. This guide walks you through three common setups that cover most developer needs:

  1. Local: run n8n with Docker (best for development and learning).
  2. VPS: run n8n with Docker behind manual Nginx + HTTPS (best for production-ish self-hosting).
  3. Coolify: deploy n8n using a PaaS-style workflow (best when you want simpler ops and repeatable deployments).

Where possible, I’ll link to official documentation so you can verify details and keep up with changes.

Before you start: what “as a service” means for n8n

Running n8n “as a service” typically implies:

  • It restarts automatically if it crashes or your server reboots.
  • Data persists across container rebuilds (volumes for the database and config).
  • It’s reachable at a stable URL (domain, or at least a stable port).
  • If exposed to the internet, it uses HTTPS, and you configure n8n’s N8N_HOST / N8N_PROTOCOL / WEBHOOK_URL so webhook callbacks work correctly.
  • You have a plan for upgrades, backups, and security.

Official docs you’ll likely reference throughout:

Note: n8n docs evolve. If a setting looks different in your installed version, always trust the official n8n environment variable reference first.


Requirements (don’t skip this)

Local (Docker)

VPS (Docker + Nginx)

  • A VPS with a supported Linux distro (commonly Ubuntu/Debian)
  • SSH access + a sudo-capable user
  • A domain name you control, with DNS A/AAAA records pointing to your VPS
  • Open ports:
    • 80/tcp (for HTTP and Let’s Encrypt challenges)
    • 443/tcp (for HTTPS)
  • Docker Engine installed on the VPS:
  • Optional but recommended: UFW firewall configured to allow 22/80/443

Coolify

  • A server where Coolify is installed (or a hosted Coolify instance)
  • Ability to deploy Docker-based apps from a repository or template
  • Domain and DNS records, if you want HTTPS and webhooks from the public internet
  • Coolify documentation for installation and reverse proxy/SSL handling:

Architecture overview (so the steps make sense)

There are two popular patterns:

  1. n8n + SQLite (simple, fine for local/dev; can work for small setups)
  2. n8n + Postgres (recommended for serious self-hosting, better concurrency and durability)

On a VPS, you’ll usually run:

  • n8n container
  • postgres container (recommended)
  • nginx on the host (or in a container) as a reverse proxy
  • TLS via Let’s Encrypt (Certbot) or via your platform (Coolify can manage this)

1) Install n8n locally with Docker (beginner-friendly)

If you’re learning n8n or building workflows locally, this is the fastest and safest setup. You can choose between a quick single-container run or a more “service-like” docker compose with persistent storage.

Option A: Quick run (good for first launch)

Official Docker guidance: https://docs.n8n.io/hosting/installation/docker/

A typical quick run looks like:

docker run -it --rm \
  -p 5678:5678 \
  -v n8n_data:/home/node/.n8n \
  docker.n8n.io/n8nio/n8n
  • -p 5678:5678 exposes n8n at http://localhost:5678
  • -v n8n_data:/home/node/.n8n persists n8n config and data in a Docker volume
  • --rm removes the container when it exits (your data remains in the volume)

This is great to verify Docker works and n8n boots, but for “run it like a service,” Compose is cleaner.

Create a folder, for example:

mkdir n8n-local && cd n8n-local

Create a compose.yml:

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n:latest
    ports:
      - "5678:5678"
    volumes:
      - n8n_data:/home/node/.n8n
    environment:
      - N8N_HOST=localhost
      - N8N_PORT=5678
      - N8N_PROTOCOL=http
      # If you use webhooks from outside localhost, you'll need a public URL.
      # For local testing with external callbacks, consider ngrok or a reverse tunnel.
    restart: unless-stopped

volumes:
  n8n_data:

Start it:

docker compose up -d
docker compose logs -f

Open:

Stop:

docker compose down

Local webhooks (important beginner gotcha)

If you use nodes that require incoming webhooks (Stripe, GitHub, etc.), localhost usually won’t work from the internet. Typical solutions:

  • Use an HTTPS tunnel like ngrok or Cloudflare Tunnel
  • Or deploy on a VPS / Coolify (next sections)

n8n has settings like WEBHOOK_URL (see env vars doc) to force webhook URLs to match your public endpoint:


2) Install n8n on a VPS with Docker + manual Nginx (production-style)

This is the “classic” self-hosting setup: Docker for app + database, Nginx as reverse proxy, Let’s Encrypt for TLS.

Step 1: Prepare your VPS

1.1 DNS

Create a DNS record:

  • A record: n8n.example.com → your VPS IPv4
  • (Optional) AAAA record for IPv6

Wait for propagation.

1.2 Install Docker on the VPS

Use the official Docker Engine install docs for your distro:

Verify:

docker --version
docker compose version

1.3 Open firewall ports

If using UFW (Ubuntu):

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status

Step 2: Create a Docker Compose stack (n8n + Postgres)

n8n’s Docker documentation includes examples and notes about persistence:

Create a directory:

sudo mkdir -p /opt/n8n && cd /opt/n8n

Create compose.yml (example with Postgres):

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

  n8n:
    image: docker.n8n.io/n8nio/n8n:latest
    restart: unless-stopped
    depends_on:
      - postgres
    environment:
      # Database config (n8n supports Postgres; see n8n docs)
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=CHANGE_ME_STRONG

      # Basic URL settings (match your public URL)
      - N8N_HOST=n8n.example.com
      - N8N_PORT=5678
      - N8N_PROTOCOL=https

      # This is commonly required so n8n generates correct external webhook URLs
      - WEBHOOK_URL=https://n8n.example.com/

      # Optional: set timezone
      - TZ=UTC

      # Behind a reverse proxy, tell n8n to trust proxy headers when needed.
      # See n8n docs for the correct variable name and usage in your version.
      # - N8N_PROXY_HOPS=1
    ports:
      # Bind only to localhost so it's not publicly exposed without Nginx
      - "127.0.0.1:5678:5678"
    volumes:
      - n8n_data:/home/node/.n8n

volumes:
  n8n_data:
  postgres_data:

Why bind 127.0.0.1:5678:5678? So n8n is only reachable locally, and the only public entry is via Nginx on 80/443.

Start:

sudo docker compose up -d
sudo docker compose logs -f

At this point, n8n should be listening on 127.0.0.1:5678.

Test from the VPS:

curl -I http://127.0.0.1:5678

Step 3: Install and configure Nginx as reverse proxy

Install Nginx (Ubuntu/Debian):

sudo apt update
sudo apt install -y nginx

Create an Nginx site config:

sudo nano /etc/nginx/sites-available/n8n.conf

Example configuration (HTTP first; TLS comes next):

server {
    listen 80;
    server_name n8n.example.com;

    location / {
        proxy_pass http://127.0.0.1:5678;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Enable it:

sudo ln -s /etc/nginx/sites-available/n8n.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Now visit:

If this works, move to HTTPS.

Step 4: Add HTTPS with Let’s Encrypt (Certbot)

Follow Certbot’s official instructions for Nginx:

On Ubuntu with snap (commonly recommended by Certbot):

sudo snap install core
sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

Request a certificate and auto-configure Nginx:

sudo certbot --nginx -d n8n.example.com

Certbot should:

  • Obtain a certificate
  • Modify the Nginx server block to redirect HTTP → HTTPS
  • Add TLS configuration

Test renewal:

sudo certbot renew --dry-run

Step 5: Verify n8n URL settings (webhooks must match)

If you see webhook URLs that still point to http://127.0.0.1:5678 or http://localhost, your environment variables are wrong (or you didn’t restart after changing them).

Key variables to review in the official doc:

After editing compose.yml, restart:

cd /opt/n8n
sudo docker compose up -d

Step 6: Run it reliably (service behaviors)

You already set:

restart: unless-stopped

That’s the Docker-native “service” behavior most people want. If the VPS reboots, Docker will start the containers again.

Upgrading n8n safely

A typical pattern:

cd /opt/n8n
sudo docker compose pull
sudo docker compose up -d
sudo docker image prune -f

If you want extra safety, take backups first.

Backups (beginner-simple approach)

At minimum, back up:

  • Postgres volume (or the database dump)
  • n8n config volume

A straightforward DB dump example:

sudo docker compose exec postgres pg_dump -U n8n n8n > n8n_backup.sql

(Exact command may vary depending on container user/permissions; the concept is: dump Postgres and store it off-server.)


3) Install n8n with Coolify (simpler ops, still Docker)

Coolify can be a big step up for beginners because it provides:

  • A UI to deploy and update apps
  • Built-in reverse proxy and SSL handling (depending on your configuration)
  • Environment variable management
  • Git-based deployments

Official Coolify docs:

Because Coolify evolves quickly, the exact clicks can differ by version, but the successful mental model stays the same: deploy the n8n container, attach persistent storage, set environment variables, and ensure you have a public URL with HTTPS.

Step 1: Install / access Coolify

Follow Coolify’s installation guide for your server:

Once installed, confirm:

  • The Coolify reverse proxy is running (Coolify often uses a proxy like Traefik under the hood, depending on version/config).
  • You can create a new “Application” (Docker-based).

Step 2: Create a new n8n application

You generally have two routes:

Route A: Deploy from a Docker image

Use the official n8n Docker image:

  • docker.n8n.io/n8nio/n8n:latest (or a pinned version tag)

In Coolify, create an app that runs that image and exposes port 5678.

Route B: Deploy from a Git repository containing a compose file

If Coolify supports “Docker Compose” apps in your version, you can store a compose.yml similar to the VPS one in a repo and let Coolify manage it.

This is useful when you want n8n + Postgres as a single stack.

Step 3: Configure persistent storage

n8n needs persistence. In Docker terms, n8n stores data in:

  • /home/node/.n8n

So in Coolify, add a persistent volume / storage mapping for that path.

If you deploy Postgres too, persist:

  • /var/lib/postgresql/data

Step 4: Configure environment variables (must match your domain)

In Coolify’s UI, set the same core environment variables you would in Compose. Refer to n8n’s official env var docs:

Common baseline for a public instance:

  • N8N_HOST=n8n.example.com
  • N8N_PROTOCOL=https
  • N8N_PORT=5678
  • WEBHOOK_URL=https://n8n.example.com/

If you’re using Postgres, add the DB variables according to n8n documentation for Postgres configuration (via env vars).

Step 5: Attach a domain and enable HTTPS

In Coolify:

  • Add the domain n8n.example.com to the application
  • Enable HTTPS/SSL (Coolify can often request and renew Let’s Encrypt certificates automatically)

After that, confirm:

  • You can access https://n8n.example.com
  • Webhooks show as https://n8n.example.com/webhook/...

Step 6: Updates and rollbacks

One of Coolify’s strengths is simplifying updates:

  • You can redeploy with a click
  • If using Git-based deploys, updates can be tied to pushes/tags
  • If using an image tag, you can change the tag and redeploy

For n8n, it’s often better to pin versions rather than always running latest, especially in production. The official Docker docs cover image usage and tags:


Common troubleshooting (across all installs)

“My webhook test works locally but not from the internet”

Most often:

  • You are not using a public URL (local only)
  • Your WEBHOOK_URL is missing or incorrect
  • Your reverse proxy is not forwarding headers correctly
  • HTTPS is missing or mixed (HTTP outside, HTTPS inside, etc.)

Start by checking what URL n8n believes it’s running on in the UI (and compare to your env vars).

“I can load the UI, but OAuth callbacks fail”

OAuth providers require exact redirect URLs. Ensure:

  • Your public URL is stable and HTTPS
  • The provider redirect URI matches n8n’s callback URL exactly
  • N8N_HOST, N8N_PROTOCOL, and WEBHOOK_URL are correct

“502 Bad Gateway from Nginx”

Usually:

  • n8n container isn’t running
  • n8n is bound to the wrong interface/port
  • Nginx is proxying to the wrong upstream (127.0.0.1:5678 must match your Docker port binding)
  • Check logs:
    • sudo docker compose logs -f n8n
    • sudo tail -f /var/log/nginx/error.log

“Data disappeared after recreating the container”

That means persistence wasn’t configured correctly:

  • Ensure your volume mapping includes /home/node/.n8n
  • On VPS, ensure named volumes exist and are referenced in the Compose file
  • On Coolify, ensure storage is configured and attached to the correct path

Which method should you choose?

If you’re a beginner developer, a practical progression is:

  • Local Docker Compose for learning and building workflows quickly.
  • VPS + Docker + Nginx when you need real webhooks, HTTPS, and full control (and you want to learn how production reverse proxies work).
  • Coolify when you want “deploy like a platform,” reduce manual Nginx/SSL work, and manage apps in a UI—without giving up Docker.

Official docs reference list (bookmark these)