VasilisKarantousis

How I Installed n8n on an Ubuntu VPS: A Complete Guide to Self-Hosting Your Automation Platform

Publication date: September 30, 2025

  • #n8n
  • #Ubuntu
  • #DigitalOcean

As a developer always looking for ways to streamline workflows and automate repetitive tasks, I recently decided to self-host n8n on my own VPS. n8n is an incredible open-source workflow automation tool that rivals services like Zapier and Make, but with the massive advantage of complete data control and no subscription fees. In this comprehensive guide, I'll walk you through exactly how I set up n8n on a subdomain of my portfolio website, vks.gr, using an Ubuntu VPS from DigitalOcean. By the end of this tutorial, you'll have your own n8n instance running securely with HTTPS, ready to automate anything you can imagine.

Why I Chose to Self-Host n8n

Before diving into the technical setup, let me share why self-hosting n8n was a game-changer for me:

  • Complete Data Privacy: All workflows and data stay on my server
  • No Monthly Fees: One-time VPS cost instead of recurring SaaS subscriptions
  • Unlimited Workflows: No restrictions on automation complexity or execution frequency
  • Custom Integrations: Freedom to add any custom nodes or modify the source code

I specifically chose DigitalOcean for my VPS because they offer reliable performance, developer-friendly tools, and transparent pricing. Their droplets (VPS instances) are easy to set up, scale effortlessly, and come with a clean, intuitive control panel—perfect for running n8n and other self-hosted apps.

💡 Want to try DigitalOcean yourself? Use my referral link to get started! You’ll receive $200 in free credit over 60 days, and once you spend $25, I’ll earn $25 too—at no extra cost to you. There’s no limit to how much credit you can give or earn through referrals, so feel free to share the love!

Prerequisites

Before we begin, you'll need:

  • A VPS running Ubuntu 22.04 or later (I recommend at least 2GB RAM)
  • A domain name with DNS management access
  • Basic familiarity with Linux command line
  • SSH access to your server
  • About 30 minutes of your time

Step 1: Initial Server Setup and Security

First, let's secure our fresh VPS. I started by logging into my Ubuntu server and creating a new user instead of using root:

ssh root@YOUR_SERVER_IP
adduser yourusername
usermod -aG sudo yourusername

Configure SSH Key Authentication

Security is paramount, so I immediately set up SSH key authentication. On your local machine, generate an SSH key if you don't have one:

ssh-keygen -t rsa -b 4096
cat ~/.ssh/id_rsa.pub

Copy the public key and add it to your server:

mkdir ~/.ssh
nano ~/.ssh/authorized_keys
# Paste your public key here
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

Set Up UFW Firewall

Ubuntu's Uncomplicated Firewall (UFW) provides essential protection:

sudo apt update
sudo apt install ufw
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw enable

Install Fail2ban for Brute Force Protection

This crucial security tool prevents automated attacks:

sudo apt install fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

Step 2: Install Docker and Docker Compose

n8n runs beautifully in Docker, providing isolation and easy management. Here's how I installed Docker on Ubuntu:

# Update package index
sudo apt-get update

# Install prerequisites
sudo apt-get install ca-certificates curl

# Add Docker's official GPG key
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add Docker repository
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Install Docker Engine and Docker Compose
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Add your user to docker group (allows running Docker without sudo)
sudo usermod -aG docker ${USER}
su - ${USER}

Verify the installation:

docker --version
docker compose version

Step 3: Set Up n8n with Docker Compose

Now for the exciting part—deploying n8n! I created a dedicated directory for the n8n configuration:

mkdir ~/n8n
cd ~/n8n
nano docker-compose.yml

Here's the production-ready Docker Compose configuration I use:

services:
  postgres:
    image: postgres:16
    restart: always
    environment:
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: your_secure_db_password_here
      POSTGRES_DB: n8n
    volumes:
      - db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n"]
      interval: 10s
      timeout: 5s
      retries: 5

  n8n:
    image: n8nio/n8n:latest
    restart: always
    environment:
      # Database Configuration
      DB_TYPE: postgresdb
      DB_POSTGRESDB_HOST: postgres
      DB_POSTGRESDB_PORT: 5432
      DB_POSTGRESDB_DATABASE: n8n
      DB_POSTGRESDB_USER: n8n
      DB_POSTGRESDB_PASSWORD: your_secure_db_password_here

      # Basic Authentication (initial setup)
      N8N_BASIC_AUTH_ACTIVE: true
      N8N_BASIC_AUTH_USER: admin
      N8N_BASIC_AUTH_PASSWORD: your_secure_admin_password

      # n8n Configuration
      N8N_HOST: localhost
      N8N_PORT: 5678
      N8N_PROTOCOL: http
      WEBHOOK_URL: http://localhost:5678/

      # Execution Configuration
      EXECUTIONS_PROCESS: main
      N8N_METRICS: true
    volumes:
      - n8n_data:/home/node/.n8n
    ports:
      - "5678:5678"
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  db_data:
  n8n_data:

Important Security Notes:

  • Replace your_secure_db_password_here with a strong password
  • Replace your_secure_admin_password with another strong password
  • Consider using environment variables or Docker secrets for production

Start n8n with:

docker compose up -d

You can now access n8n at http://YOUR_SERVER_IP:5678!

Step 4: Configure Nginx as a Reverse Proxy

To serve n8n on a proper domain with HTTPS, I set up Nginx as a reverse proxy:

sudo apt install -y nginx
sudo systemctl start nginx
sudo systemctl enable nginx

Create the n8n site configuration:

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

Add this configuration (replace n8n.yourdomain.com with your subdomain):

server {
    listen 80;
    server_name n8n.yourdomain.com www.n8n.yourdomain.com;

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;

    location / {
        proxy_pass http://localhost:5678;
        proxy_http_version 1.1;
        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;

        # WebSocket support (required for n8n)
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Timeout settings for long-running workflows
        proxy_connect_timeout 300;
        proxy_send_timeout 300;
        proxy_read_timeout 300;
    }
}

Enable the site:

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

Step 5: Configure DNS

For my setup on n8n.vks.gr, I added these DNS records through my domain registrar:

  • A Record for n8n subdomain: Name: n8n Type: A Value: YOUR_SERVER_IP TTL: 60

  • A Record for www.n8n subdomain: Name: www.n8n Type: A Value: YOUR_SERVER_IP TTL: 60

DNS propagation usually takes 5–30 minutes.

Step 6: Enable HTTPS with Let's Encrypt

Security is non-negotiable for any web application. I used Certbot to get free SSL certificates:

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d n8n.yourdomain.com -d www.n8n.yourdomain.com

Follow the prompts to:

  • Enter your email address
  • Agree to the terms of service
  • Choose whether to redirect HTTP to HTTPS (recommended: yes)

After this, update your Nginx configuration for optimal HTTPS performance:

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

Here's the enhanced HTTPS configuration:

# Redirect non-www to www (HTTPS)
server {
    listen 443 ssl http2;
    server_name n8n.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/n8n.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/n8n.yourdomain.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    return 301 https://www.n8n.yourdomain.com$request_uri;
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name n8n.yourdomain.com www.n8n.yourdomain.com;
    return 301 https://www.n8n.yourdomain.com$request_uri;
}

# Main server block (www HTTPS only)
server {
    listen 443 ssl http2;
    server_name www.n8n.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/n8n.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/n8n.yourdomain.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    # Security headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    location / {
        proxy_pass http://localhost:5678;
        proxy_http_version 1.1;
        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;

        # WebSocket support
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Extended timeouts for long workflows
        proxy_connect_timeout 300;
        proxy_send_timeout 300;
        proxy_read_timeout 300;

        # Buffer settings
        proxy_buffering off;
        proxy_request_buffering off;
    }
}

Reload Nginx:

sudo nginx -t
sudo systemctl reload nginx

Step 7: Update n8n Environment for Production

Now that HTTPS is configured, update your n8n Docker Compose file to reflect the proper webhook URL:

cd ~/n8n
nano docker-compose.yml

Update these environment variables in the n8n service:

N8N_PROTOCOL: https
WEBHOOK_URL: https://www.n8n.yourdomain.com/
N8N_HOST: www.n8n.yourdomain.com

Restart n8n:

docker compose down
docker compose up -d

Step 8: First Login and Configuration

Navigate to https://www.n8n.yourdomain.com and you'll see the n8n login screen. Use the credentials you set in the Docker Compose file.

On first login, n8n will guide you through:

  • Creating your owner account
  • Setting up your workspace
  • Connecting to external services

Performance Optimization Tips

After running n8n for several months, here are my optimization recommendations:

1. Database Maintenance

Create a maintenance script for PostgreSQL:

nano ~/n8n/db-maintenance.sh

Add the following:

#!/bin/bash
docker exec n8n-postgres-1 psql -U n8n -d n8n -c "VACUUM ANALYZE;"
docker exec n8n-postgres-1 psql -U n8n -d n8n -c "REINDEX DATABASE n8n;"

Make it executable and add to crontab:

chmod +x ~/n8n/db-maintenance.sh
crontab -e
# Add: 0 3 * * 0 /home/yourusername/n8n/db-maintenance.sh

2. Log Rotation

Configure Docker log rotation in /etc/docker/daemon.json:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

3. Monitoring

Set up basic monitoring with Docker stats:

docker stats --no-stream

Consider implementing Prometheus + Grafana for advanced monitoring.

Troubleshooting Common Issues

WebSocket Connection Failed

If you see WebSocket errors, ensure your Nginx configuration includes:

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

Workflows Not Executing

Check the Docker logs:

docker compose logs -f n8n

Common fixes:

  • Increase memory limits if needed
  • Verify webhook URL is correctly set
  • Check firewall rules

SSL Certificate Renewal

Certbot should auto-renew, but you can test renewal with:

sudo certbot renew --dry-run

Security Best Practices

  • Regular Updates: Keep n8n and all dependencies updated
    docker compose pull
    docker compose up -d
    
  • Backup Strategy: Implement regular backups
    docker compose down
    tar -czf n8n-backup-$(date +%Y%m%d).tar.gz ~/n8n
    docker compose up -d
    
  • API Key Management: Use n8n's credential management for API keys
  • Network Isolation: Consider using Docker networks to isolate services
  • Audit Logs: Enable n8n audit logs for compliance

Conclusion

Setting up n8n on my own VPS was one of the best infrastructure decisions I've made. Not only do I have complete control over my automation workflows, but I'm also saving significantly compared to SaaS alternatives. The setup process, while detailed, is straightforward enough for any developer comfortable with basic Linux administration.

The beauty of self-hosting is that I can create unlimited workflows without worrying about execution limits or monthly fees. If you're considering workflow automation for your projects, I highly recommend giving self-hosted n8n a try.

🚀 Ready to launch your own n8n instance? Get started on DigitalOcean using my referral link! You’ll get $200 in free credit, and I’ll earn $25 once you spend $25—helping me keep this guide free and up to date. Win-win!

Next Steps

Now that you have n8n running, explore:

  • Connecting your favorite apps and services
  • Creating your first automation workflow
  • Setting up custom nodes for specific needs
  • Implementing advanced features like error workflows
  • Exploring the n8n community for workflow inspiration

Have questions or need help with your n8n setup? Feel free to reach out through my portfolio at vks.gr. Happy automating!

This guide is based on my personal experience setting up n8n for production use. Your specific requirements may vary, so always consider your security and performance needs when deploying any self-hosted solution.

Do you like what you see?

It's crafted with Next.js, TailwindCSS, Framer Motion, and a mix of dangerous magic and chemical reactions.

Download