Install n8n on Ubuntu Server Step by Step

Ahmed
0

Install n8n on Ubuntu Server Step by Step

I’ve installed n8n on fresh Ubuntu VPS instances where one small misstep (like a missing volume or a wrong webhook URL) broke automations later.


Install n8n on Ubuntu Server Step by Step to get a stable, secure setup with persistent data, HTTPS, and clean updates.


Install n8n on Ubuntu Server Step by Step

What you need before you start

Have these ready so you don’t pause mid-install:

  • An Ubuntu server (22.04 LTS or 24.04 LTS) with SSH access and a sudo user.
  • At least 1 GB RAM for light usage (2 GB+ is better if you run many workflows).
  • (Recommended) A domain or subdomain pointing to your server’s public IP if you want HTTPS and reliable webhooks.

If you want to review the official project pages once (and only once), use these links: n8n, Docker on Ubuntu, Ubuntu, Caddy.


Step 1: Update Ubuntu and set a safe baseline

Start with upgrades, then enable a firewall. This reduces “mystery” failures later.

sudo apt update

sudo apt -y upgrade
sudo reboot

After reconnecting by SSH, enable UFW and allow SSH so you don’t lock yourself out.

sudo apt -y install ufw

sudo ufw allow OpenSSH sudo ufw enable
sudo ufw status

Real-world pitfall (and fix): UFW is easy to misconfigure when you’re moving fast. If you plan to expose n8n to the internet through HTTPS, you’ll also allow ports 80/443 later (not 5678).


Step 2: Install Docker Engine and Docker Compose (plugin)

Docker Compose is the cleanest way to run n8n on a server because it makes upgrades and restarts predictable.

sudo apt -y install ca-certificates curl gnupg

sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt update
sudo apt -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Verify installation:

docker --version

docker compose version
sudo systemctl status docker --no-pager

(Optional but convenient) run Docker without sudo:

sudo usermod -aG docker $USER

newgrp docker

Real-world pitfall (and fix): Docker group membership grants powerful access. If this is a shared server, keep Docker restricted to admin users only, and prefer sudo.


Step 3: Create the n8n directory and persistent volumes

The fastest way to lose an n8n instance is running it without persistent storage. Use a dedicated directory and volumes.

sudo mkdir -p /opt/n8n

sudo chown -R $USER:$USER /opt/n8n
cd /opt/n8n

Step 4: Create a production-ready docker-compose.yml

This setup keeps n8n bound to localhost so the public internet can’t hit port 5678 directly. You’ll publish it safely via HTTPS in the next step.


First, generate and save a strong encryption key. This key protects stored credentials. If you change it later, previously saved credentials can become unusable.

openssl rand -hex 32

Create the Compose file:

nano docker-compose.yml
services:

n8n: image: n8nio/n8n:latest restart: unless-stopped ports: - "127.0.0.1:5678:5678" environment: - TZ=America/New_York - N8N_HOST=n8n.example.com - N8N_PORT=5678 - N8N_PROTOCOL=https - WEBHOOK_URL=https://n8n.example.com/ - N8N_PROXY_HOPS=1 - N8N_ENCRYPTION_KEY=PASTE_YOUR_64_CHAR_HEX_KEY_HERE volumes: - n8n_data:/home/node/.n8n volumes:
n8n_data:

Replace:

  • TZ with your preferred US timezone.
  • N8N_HOST and WEBHOOK_URL with your real domain.
  • N8N_ENCRYPTION_KEY with the value you generated and stored safely.

Real-world pitfall (and fix): The most common “everything works except webhooks” problem is a wrong webhook base URL behind a reverse proxy. Setting WEBHOOK_URL and N8N_PROXY_HOPS avoids broken callback URLs when n8n is internally on 5678 but publicly on 443.


Step 5: Start n8n and confirm it’s healthy

Bring the stack up, then confirm the container is running.

docker compose up -d

docker compose ps
docker compose logs -f --tail=100

At this point, n8n is listening on 127.0.0.1:5678 only. That’s intentional. Next you’ll publish it through HTTPS.


Step 6: Add HTTPS with a reverse proxy (Caddy)

Caddy is a strong fit on Ubuntu servers because it can automatically handle TLS certificates. You’ll proxy from 443 to n8n on localhost:5678.


Install Caddy and allow web ports:

sudo apt -y install caddy

sudo ufw allow 80
sudo ufw allow 443

Create a Caddyfile:

sudo nano /etc/caddy/Caddyfile
n8n.example.com {

reverse_proxy 127.0.0.1:5678
}

Reload Caddy:

sudo caddy validate --config /etc/caddy/Caddyfile

sudo systemctl reload caddy

Now open https://n8n.example.com and complete the initial setup in the UI.


Real-world pitfall (and fix): If your DNS isn’t pointing correctly, Caddy can’t complete TLS issuance. Confirm the A record resolves to your server IP, then retry. Keep n8n bound to localhost so random traffic never hits 5678 directly.


Docker vs npm install: what you should use on a server

Option Best for Main downside How to reduce the downside
Docker Compose Production servers, predictable upgrades, simple restarts Extra layer (Docker) to secure and update Pin versions when needed, schedule updates, keep backups of volumes
npm / Node install Custom Node environments and advanced host-level tuning More moving parts (Node versions, process manager, updates) Use a process manager and strict version control for Node and n8n

Hardening that actually matters for n8n

  • Keep port 5678 private: bind it to 127.0.0.1 as shown, and expose only 80/443 publicly.
  • Protect the encryption key: store it in a password manager. If it changes, stored credentials can break.
  • Use a dedicated domain: it prevents mixed-content and webhook confusion across environments.
  • Limit server access: prefer SSH keys, disable password SSH if possible, and keep sudo access minimal.

Backups and upgrades without drama

When n8n becomes business-critical, you need a repeatable upgrade pattern and a fast rollback plan.


Backup the named volume regularly (store the resulting archive off the server):

cd /opt/n8n

docker compose down docker run --rm -v n8n_n8n_data:/data -v $(pwd):/backup ubuntu tar czf /backup/n8n_data_backup.tar.gz -C /data .
docker compose up -d

Upgrade n8n safely:

cd /opt/n8n

docker compose pull docker compose up -d
docker image prune -f

Real-world pitfall (and fix): “latest” is convenient, but it can surprise you. If you run revenue-impacting workflows, pin the image tag to a specific n8n version and upgrade intentionally after a backup.


Troubleshooting the top issues

  • UI loads, but webhooks show localhost:5678: confirm WEBHOOK_URL matches your HTTPS domain and ends with a trailing slash.
  • Reverse proxy works, but integrations fail callbacks: re-check the domain in N8N_HOST and WEBHOOK_URL, then restart the container.
  • Credentials suddenly fail after a change: verify N8N_ENCRYPTION_KEY never changed between deployments.
  • Can’t access n8n publicly: confirm DNS points to the server, UFW allows 80/443, and Caddy is running.

FAQ: deep answers for common n8n on Ubuntu questions

Can you install n8n on Ubuntu without Docker?

Yes, but you’ll need a managed Node environment, a process manager, and a tighter upgrade routine. On most Ubuntu servers, Docker Compose stays easier to maintain because your runtime, ports, volumes, and restarts are all declared in one place.


What’s the safest way to expose n8n on the internet?

Keep n8n on 127.0.0.1:5678 and expose only HTTPS through a reverse proxy on 443. This prevents direct scanning of n8n and forces all traffic through TLS.


How do you fix n8n webhook URLs when using a reverse proxy?

Set WEBHOOK_URL to your public HTTPS base URL and keep it consistent. This ensures n8n registers correct callback URLs with providers and shows the right URLs in the editor.


Do you need a domain for n8n?

If you use triggers that rely on inbound webhooks (Stripe, GitHub, Slack, Calendars, and many others), a domain is strongly recommended because it gives you stable HTTPS URLs and reduces configuration mismatches.


Where is n8n data stored in this setup?

All persistent n8n state is stored inside the Docker named volume (n8n_data). That’s why the volume backup is non-negotiable if you care about workflows, credentials, and history.


How do you move n8n to a new server?

Export your volume backup, copy docker-compose.yml, keep the same N8N_ENCRYPTION_KEY, then restore the volume on the new machine. If the encryption key changes, credentials can stop working even if workflows import correctly.



Conclusion

Once n8n is running behind HTTPS with persistent storage and a fixed webhook URL, it behaves like a production service instead of a fragile hobby install. Keep your encryption key safe, back up the volume, upgrade intentionally, and you’ll have an Ubuntu-based n8n instance you can trust for real workflows.


Post a Comment

0 Comments

Post a Comment (0)