AutomateSmith

Menu

Automation Tutorials

How to self-host n8n on a cheap VPS with Docker

A step-by-step way to run n8n on a small Linux server with Docker Compose, HTTPS, backups and the checks people skip.

By Sandun BalasooriyaLast updated 7 min read

This article contains affiliate links. If you buy through them, AutomateSmith may earn a commission at no extra cost to you. Opinions are Sandun Balasooriya's. Read the affiliate disclosure.

On this page
  1. What you are building
  2. 1. Pick a server without guessing a price
  3. 2. Install Docker the dull way
  4. 3. Create the project directory and the environment file
  5. 4. Use the official Compose file, and understand it
  6. 5. Start it, then log in once
  7. 6. Backups before the first real workflow
  8. 7. Updates
  9. 8. What to leave alone on week one
  10. Frequently asked questions

Self-hosting n8n is worth it when you want the workflows on a machine you control, and you are willing to be the person who updates that machine. It is a bad idea when the appeal is only “I heard it is free”. The software may cost less than a large cloud plan. The server, the domain, the backups and the evening you spend when TLS breaks are not free.

n8n’s installation docs are explicit: self-hosting is for people comfortable running servers, and mistakes can lead to data loss, security problems and downtime. If that is not a fair description of you, stop here and use n8n Cloud or another tool. This guide follows their Docker Compose path as checked in October 2026. If their page and this article disagree, follow their page. The file changes.

What you are building

One small Linux server. Docker and Docker Compose. A subdomain such as n8n.example.com pointing at the server. Traefik, in the official example, requests a certificate and sends HTTPS traffic to n8n. n8n itself listens on port 5678 only on localhost, so the editor is not published raw to the internet. Data lives in a Docker volume. You will back that volume up.

You do not need Kubernetes for this. A single VPS is the right size until you have a reason to split it.

1. Pick a server without guessing a price

Hetzner Cloud and DigitalOcean are the two providers I see small teams use most often. n8n also documents several hosts. I am not quoting a monthly price, because it will be wrong by the time you read this. Open the provider’s page, choose a current plan with at least 2 GB of RAM and a bit of disk (a 20 GB-class disk is plenty to start), and put the server in a region you can explain to your customers.

Create the server with an SSH key. Do not leave password login as the way you get in if you know how to turn it off. Ubuntu LTS is a boring, good choice.

Point DNS before you care about certificates. Create an A record for the subdomain to the server’s IPv4 address. If you also use IPv6, add an AAAA record. Certificates fail in confusing ways when DNS is still propagating, so check the record from your laptop before you blame Traefik.

2. Install Docker the dull way

On the server, install Docker Engine and the Compose plugin by following Docker’s own documentation for your distribution: Install Docker Engine. Avoid a random script from a forum. When it is done:

docker --version
docker compose version

Both commands should print a version. If you want to run Docker without sudo, n8n’s Compose guide shows adding your user to the docker group and refreshing the session. That convenience is also root-equivalent on the box. Do it only for the account that is supposed to have that power.

Open the cloud firewall, or ufw if you use it, for SSH, HTTP and HTTPS only. A sensible start is ports 22, 80 and 443. Do not publish 5678 to the world. The Compose file binds it to localhost on purpose.

3. Create the project directory and the environment file

n8n’s current Compose guide uses a directory called n8n-compose, an .env file, and a local-files directory. The shape is:

mkdir -p ~/n8n-compose/local-files
cd ~/n8n-compose

Create .env with your real domain and an email address Let’s Encrypt can use. The timezone should be the one your schedules should follow. For a UK business that is often Europe/London.

DOMAIN_NAME=example.com
SUBDOMAIN=n8n
GENERIC_TIMEZONE=Europe/London
SSL_EMAIL=[email protected]

SUBDOMAIN and DOMAIN_NAME together become the public address, https://n8n.example.com in this example. Do not commit this file to a git repository. It is not a secret-heavy file, but it does not belong in public source control next to a later password.

4. Use the official Compose file, and understand it

Create compose.yaml. The following matches the structure of n8n’s Docker Compose guide as published when this article was updated. Copy from their page if anything here has drifted: Use Docker Compose.

services:
  traefik:
    image: "traefik"
    restart: always
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"
      - "--certificatesresolvers.mytlschallenge.acme.email=${SSL_EMAIL}"
      - "--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - traefik_data:/letsencrypt
      - /var/run/docker.sock:/var/run/docker.sock:ro

  n8n:
    image: n8nio/n8n
    restart: always
    ports:
      - "127.0.0.1:5678:5678"
    labels:
      - traefik.enable=true
      - traefik.http.routers.n8n.rule=Host(`${SUBDOMAIN}.${DOMAIN_NAME}`)
      - traefik.http.routers.n8n.tls=true
      - traefik.http.routers.n8n.entrypoints=web,websecure
      - traefik.http.routers.n8n.tls.certresolver=mytlschallenge
      - traefik.http.middlewares.n8n.headers.SSLRedirect=true
      - traefik.http.middlewares.n8n.headers.STSSeconds=315360000
      - traefik.http.middlewares.n8n.headers.browserXSSFilter=true
      - traefik.http.middlewares.n8n.headers.contentTypeNosniff=true
      - traefik.http.middlewares.n8n.headers.forceSTSHeader=true
      - traefik.http.middlewares.n8n.headers.SSLHost=${DOMAIN_NAME}
      - traefik.http.middlewares.n8n.headers.STSIncludeSubdomains=true
      - traefik.http.middlewares.n8n.headers.STSPreload=true
      - traefik.http.routers.n8n.middlewares=n8n@docker
    environment:
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
      - N8N_HOST=${SUBDOMAIN}.${DOMAIN_NAME}
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - NODE_ENV=production
      - N8N_WEBHOOK_URL=https://${SUBDOMAIN}.${DOMAIN_NAME}/
      - N8N_RESTRICT_FILE_ACCESS_TO=/files
      - GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
      - TZ=${GENERIC_TIMEZONE}
    volumes:
      - n8n_data:/home/node/.n8n
      - ./local-files:/files

volumes:
  n8n_data:
  traefik_data:

Read that file before you start it. Traefik is the proxy. It listens on 80 and 443, redirects HTTP to HTTPS, and asks Let’s Encrypt for a certificate using the email in .env. The Docker socket is mounted read-only so Traefik can see labelled containers. That socket is powerful. Do not also publish Traefik’s API port.

The official example includes --api.insecure=true and does not publish port 8080. Leave it unpublished. If you do not need the Traefik dashboard, you can remove that flag once certificates work.

The image line below is n8nio/n8n, which is what n8n’s Compose guide uses. Pin a version tag from their release notes if you want upgrades to be a choice rather than a surprise. Check the Docker install page if the image name has moved.

N8N_WEBHOOK_URL must be the public HTTPS address, with the trailing slash their examples use. If webhooks arrive at the wrong host, executions will look fine in the editor and fail in the real world. n8n’s Compose guide notes that N8N_WEBHOOK_URL applies from n8n 2.30.0, and that earlier versions use WEBHOOK_URL. If webhooks misbehave, set the name their page documents for the version you are running. N8N_RESTRICT_FILE_ACCESS_TO keeps file nodes inside the shared folder.

The volume n8n_data holds the SQLite database and the encryption key. local-files is a folder you can reach from n8n at /files if you later enable file nodes. Do not point those nodes at your SSH directory.

5. Start it, then log in once

From ~/n8n-compose:

docker compose up -d
docker compose ps
docker compose logs -f n8n

Wait until n8n reports that it is ready, then open https://n8n.example.com. The first visit asks you to create the owner account. Use a long password and store it properly. There is no “small site” exception for that account: it can read credentials for every workflow you add.

If the certificate fails, check three things in order: does the DNS record point at this server, are ports 80 and 443 open, and did Traefik log an ACME error? docker compose logs traefik is the place to look. Do not “fix” a certificate problem by switching the site to plain HTTP.

6. Backups before the first real workflow

A backup you have not restored is a rumour. For a single-node volume, a simple copy is enough to start. Stop n8n, archive the volume, start it again, and copy the archive off the server.

docker compose stop n8n
docker run --rm -v n8n-compose_n8n_data:/data -v "$PWD":/backup alpine \
  tar czf /backup/n8n-data.tgz -C /data .
docker compose start n8n

The volume name depends on the Compose project name. docker volume ls will show it. Copy n8n-data.tgz to somewhere that is not the same VPS. A weekly run of that command, plus a check that the file is not tiny, will save you once. Write down how to restore it. Restoring is the same tar in the other direction into an empty volume, done while n8n is stopped.

Also snapshot the server in the provider’s panel before upgrades if they offer snapshots. Check what that snapshot costs. It is a second copy, not a reason to skip the volume archive, because you want the n8n data even if you move providers.

7. Updates

n8n ships often. From the Compose directory, the documented update is:

docker compose pull
docker compose down
docker compose up -d

Read the release note when the major version changes. Take the backup first. down removes containers, not named volumes, so n8n_data survives. Do not run docker compose down -v unless you mean to delete the data.

After an update, open the editor, run one harmless workflow, and check a webhook if you depend on one. That is the whole smoke test. If it fails, you still have the backup and the previous image in Docker’s cache only if you have not pruned it. Do not prune images until the new version has survived a day.

8. What to leave alone on week one

Do not expose the Traefik dashboard. Do not put Postgres on the same compose file until the SQLite setup is boring and backed up. Do not install five other apps on this VPS “while you are here” if this box holds client data. Do not turn on community nodes you have not read. Do not paste production API keys into a workflow you then export into a public git repository. Export without credentials, or keep the repository private.

When you do add an AI key, create it in the provider’s dashboard, set a spending cap, and store it as an n8n credential. The email assistant guide shows the shape I want: drafts, not unsupervised sending. A first chat agent, with memory and tools that only read, is the AI Agent tutorial.

Pros

  • Workflow data and credentials stay on a server you choose
  • The official Compose path gives you HTTPS without hand-writing Nginx
  • One small VM is enough for a single team

Cons

  • You own updates, backups, firewall rules and the encryption key
  • A cheap box with no backup is worse than a paid cloud plan
  • SQLite on one volume will not be the right database forever if executions pile up

The Compose steps above follow n8n’s Docker Compose guide. Use that page if a label has been renamed.

Once the editor loads over HTTPS, build one useful workflow and stop. A good second project is lead capture into a CRM, or the comparison if you are still wondering whether the team should have stayed on Make: Make vs Zapier vs n8n.

Frequently asked questions

How much RAM do I need?

Start with at least 2 GB of RAM for Docker, Traefik and one n8n instance. A 1 GB server often feels tight once the editor is open. This is a practical starting point, not a benchmark. Move up a size if the box starts swapping, and check the provider's current price before you order.

Can I use the tunnel instead of a domain?

n8n's tunnel is for local development. Do not use it as the public address of a business workflow. Point a real subdomain at the server and let Traefik get a certificate.

What happens if I lose the server?

Workflows and saved credentials depend on the n8n data volume, including the encryption key. If you lose that volume and you do not have a copy of the key, stored credentials are not recoverable. Back the volume up before you celebrate the first workflow.

Should I put Postgres on the same box?

The official Compose example uses SQLite in the n8n volume, which is a fair start for a single small team. Move to Postgres when executions grow or you want a database you already know how to back up. Follow n8n's current database environment variables rather than an old blog snippet.

Is self-hosting the right default?

No. n8n says self-hosting is for people who can run servers, and that mistakes can cause data loss and downtime. If that is not you, use n8n Cloud or a tool you do not have to patch. The comparison guide covers that choice.

Get the next practical guide

Short notes when a new tutorial goes up. No invented case studies in the inbox either.

Occasional notes on AI automation for small teams. No drip course. Unsubscribe any time.