After a thorough research and testing process powered by Gemini, I’ve created what I believe to be the definitive guide to a production-ready n8n installation on Ubuntu with Docker. I’ve personally run and verified every step of this process, and I can confirm it works flawlessly. This guide focuses on best practices to ensure your self-hosted n8n instance is secure, reliable, and ready for long-term use.
n8n Installation
The deployment of a self-hosted workflow automation platform, such as n8n, requires a strategic approach that prioritizes security, data integrity, and long-term operational sustainability. While a basic installation can be achieved with minimal effort, a robust, production-grade setup necessitates a more deliberate architecture. This report provides a comprehensive, step-by-step guide to installing n8n on an Ubuntu server using Docker, adhering to industry best practices for a resilient and secure deployment.
Part I: Foundational Architecture and Component Selection
A production-ready n8n stack is not a single container but a collection of interconnected services designed to work in concert. The architectural decisions made at this stage are critical for ensuring the system’s reliability, scalability, and security.
1.1 System Prerequisites and Architectural Decisions
A successful deployment is contingent upon a correctly provisioned host environment. The following prerequisites are considered the foundation for a best-practice installation:
- A Virtual Private Server (VPS) running a modern version of Ubuntu (e.g., 20.04 or 22.04).
- A domain name with a DNS A record pointed to the server’s public IP address.
- Access to the server via SSH as a user with
sudo
privileges or as the root user. - Firewall rules that permit traffic on ports 80 and 443 for public access, as well as port 22 for SSH access. Port 5678 should be blocked from external access and only permitted internally.
The choice to use Docker is a fundamental architectural decision for a production environment. Although alternative methods, such as a direct npm
installation, exist, containerization offers significant advantages for self-hosted applications. Docker encapsulates n8n and its dependencies within an isolated, portable container, eliminating conflicts with other applications and simplifying dependency management (e.g., Node.js versions and required build tools). This isolation ensures that n8n runs in a consistent environment, from development to production, which is crucial for reliability and simplifies future maintenance and updates. The use of Docker Compose further streamlines this process by allowing the entire multi-container stack to be defined and managed through a single configuration file.
1.2 The Database: Moving from SQLite to PostgreSQL for Production
By default, a simple docker run
command for n8n uses a file-based SQLite database to store all critical data, including workflows, credentials, and execution history. While this approach is adequate for a basic local development or testing environment, it introduces significant risks in a production setting.
The core limitation of SQLite is its lack of robust concurrency handling. In a production environment with concurrent workflow executions, multiple processes or threads will attempt to write to the same database file simultaneously. This can lead to file-locking issues, performance bottlenecks, and, in severe cases, data corruption. Furthermore, scaling a SQLite-based setup is impossible, as the database is a single point of failure that cannot be shared or replicated across multiple machines.
For these reasons, the use of a robust, client-server database is a non-negotiable requirement for a production-ready n8n deployment. PostgreSQL is the widely recommended solution that n8n natively supports. A PostgreSQL server is architecturally designed to manage concurrent transactions and ensure data integrity, providing the stability and reliability required for mission-critical automation workloads. Deploying n8n alongside a dedicated PostgreSQL container using Docker Compose is the established best practice for achieving this level of data durability.
A comparison of the two database options is provided below.
Category | SQLite (Default for Docker Run) | PostgreSQL (Recommended for Production) |
Use Case | Local development, simple testing | Production, multi-user environments |
Concurrency | Limited. Prone to file-locking issues | High. Architecturally designed for concurrent transactions |
Data Integrity | Vulnerable to corruption under high load | Robust, ACID-compliant transactions |
Backup Complexity | Requires backing up a single file | Requires database-specific tools (pg_dump ) |
Performance | Sub-optimal for high-I/O or high-concurrency workloads | High performance, scalable |
Recommendation | Not suitable for any long-term or critical use case | Required for stability and data reliability |
1.3 The Reverse Proxy: A Non-Negotiable Security Layer
Directly exposing an application’s internal port (in n8n’s case, port 5678) to the public internet is a significant security vulnerability. This practice bypasses essential security measures and should be avoided in all production scenarios. A reverse proxy serves as an intermediary layer between the public internet and the n8n container, providing a critical set of services that ensure security and correct functionality.
The primary function of a reverse proxy is to handle SSL/TLS encryption. The n8n application uses secure cookies by default, and its webhooks rely on secure communication. Without a reverse proxy configured to provide an HTTPS connection, the n8n web interface may display browser security warnings, and more importantly, webhook functionality, which is essential for triggering workflows from external services, will not operate securely. A reverse proxy also handles port forwarding, routing external requests on standard ports (e.g., 80 and 443) to the internal port of the n8n container.
When selecting a reverse proxy, two of the most popular choices for a Dockerized environment are Nginx and Traefik. Nginx is a mature, battle-tested solution that offers a high degree of manual control through static configuration files. Traefik, on the other hand, is a more modern, container-native solution that automates service discovery and SSL certificate management using Docker labels.
A comparison of Nginx and Traefik is provided below to assist in the selection process.
Category | Nginx | Traefik |
Configuration Style | Manual configuration files | Automated via Docker labels |
Use Case | Ideal for users who prefer granular control over their proxy settings | Best for dynamic environments with multiple services and for simplifying SSL management |
Learning Curve | Moderate; requires knowledge of Nginx syntax | Lower for basic use; higher for advanced features |
SSL Management | Manual with Certbot (certbot --nginx ) | Automatic via Docker labels |
Pros | Well-documented, high performance, powerful for complex setups | Highly automated, simplifies configuration, great for microservices |
Cons | Requires manual configuration for each new service | May have a steeper learning curve for advanced use cases |
Part II: The Definitive Step-by-Step Production Deployment
This section provides the explicit, annotated commands and configuration files required to deploy the recommended production stack.
2.1 Step 1: Preparing the Host Environment
The first action is to create a dedicated directory for the n8n project and establish the correct file permissions for persistent data storage. This is a subtle but critical security and operational best practice. The n8n Docker image is configured to run its processes as a non-root user with a User ID (UID) of 1000. For the n8n container to be able to write to the host’s file system, the host directories must be owned by a user with this same UID. Failure to do so will result in permission errors and the container’s inability to start.
First, connect to the server via SSH and create the project directory:
Bash
mkdir ~/n8n && cd ~/n8n
Next, create a subdirectory for n8n’s persistent data and set the correct ownership and permissions.
Bash
mkdir n8n_data
sudo chown -R 1000:1000 n8n_data
This command recursively changes the ownership of the n8n_data
directory to the user with UID 1000 and group ID (GID) 1000, ensuring the n8n container can read from and write to its persistent data volume.
2.2 Step 2: Crafting the Production docker-compose.yml
The docker-compose.yml
file is the central blueprint for the entire n8n stack. This file defines two key services: db
for the PostgreSQL database and n8n
for the workflow automation application. The configuration below implements all the architectural best practices discussed in Part I.
Create a new file named docker-compose.yml
in the ~/n8n
directory using a text editor such as nano
.
Bash
sudo nano docker-compose.yml
Paste the following content into the file, ensuring the environment variables are replaced with secure, randomly generated values.
YAML
services:
db:
image: postgres:14
restart: always
environment:
- POSTGRES_USER=n8n
- POSTGRES_PASSWORD=n8npass
- POSTGRES_DB=n8n
volumes:
- postgres_data:/var/lib/postgresql/data
n8n:
image: n8nio/n8n
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678"
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=db
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=n8npass
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=admin
- N8N_BASIC_AUTH_PASSWORD=StrongPasswordHere
- N8N_HOST=n8n.yourdomain.com
- WEBHOOK_TUNNEL_URL=https://n8n.yourdomain.com
- N8N_ENCRYPTION_KEY=StrongEncryptionKey
depends_on:
- db
volumes:
- n8n_data:/home/node/.n8n
volumes:
postgres_data:
n8n_data:
Key Annotations:
db
service: This block configures the PostgreSQL container. It uses the officialpostgres:14
image and sets environment variables for the user, password, and database name. A named volume,postgres_data
, is mounted to/var/lib/postgresql/data
to ensure the database contents persist even if the container is recreated.n8n
service: This block defines the n8n container. Thedepends_on: - db
directive is critical as it ensures the PostgreSQL database container is running and healthy before the n8n service attempts to start.environment
variables: These variables configure n8n to connect to the PostgreSQL service using the credentials defined in thedb
service. Additionally,N8N_BASIC_AUTH_...
variables enforce a login screen for the n8n editor, a vital security measure. TheN8N_HOST
andWEBHOOK_TUNNEL_URL
variables ensure that n8n correctly generates URLs for its webhooks and that they are publicly accessible via the designated domain.3N8N_ENCRYPTION_KEY
: This variable is used to encrypt sensitive credentials stored in the n8n database. If this variable is not explicitly set, n8n will generate a new key on its first launch. It is a best practice to define a key and back it up. Without a backed-up key, all encrypted credentials would be irrecoverably lost upon container recreation.volumes
: This section defines the named volumes that are used by the services. This is a critical practice for data persistence, ensuring that workflows, credentials, and the database remain intact across container restarts and updates.
2.3 Step 3: Bringing the Stack to Life
With the docker-compose.yml
file in place, the entire stack can be launched with a single command.
Navigate to the ~/n8n
directory and execute the following command:
Bash
docker compose up -d
The up
command will read the docker-compose.yml
file, pull the necessary Docker images, and create and start the containers. The -d
flag runs the containers in “detached” mode, allowing them to run in the background.
To verify that the containers are running, use the following command:
Bash
docker ps
The output should list both the n8n
and db
containers with a status of Up
. If a container is not running, its status will be
Exited
. To troubleshoot, inspect the container logs with:
Bash
docker logs <container_name>
Part III: Securing and Hardening the n8n Stack
A functional deployment is only the first step. For a true production-ready environment, the stack must be secured against external and internal threats.
3.1 Step 4: Securing with a Reverse Proxy and SSL
This step is arguably the most critical for production deployments. It provides two distinct pathways for configuring a reverse proxy, Nginx or Traefik.
Option A: Nginx and Certbot
Nginx is a robust and widely used web server and reverse proxy. When paired with Certbot, it provides a powerful and transparent way to secure n8n with an SSL certificate from Let’s Encrypt.
- Install Nginx and Certbot:Bash
sudo apt update sudo apt install nginx certbot python3-certbot-nginx -y
- Configure Nginx Reverse Proxy: Create a new Nginx configuration file for n8n.Bash
sudo nano /etc/nginx/sites-available/n8n.conf
Paste the following configuration, replacingn8n.yourdomain.com
with the actual domain name. This configuration not only proxies traffic but also explicitly enables WebSocket support. This is a crucial detail, as n8n’s user interface relies on WebSockets for real-time updates. Without this configuration, the UI will load but display a persistent “Connection lost” error, even with a valid HTTPS connection.14Nginxserver { listen 80; server_name n8n.yourdomain.com; location / { proxy_pass http://localhost:5678; 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; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } }
- Enable and Test the Configuration:Bash
sudo ln -s /etc/nginx/sites-available/n8n.conf /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl restart nginx
- Obtain and Install SSL Certificate with Certbot:Bash
sudo certbot --nginx -d n8n.yourdomain.com --non-interactive --agree-tos -m your-email@example.com
Certbot will automatically modify the Nginx configuration to enable HTTPS and manage certificate renewals.
Option B: Traefik
Traefik is an alternative that simplifies the reverse proxy and SSL setup through a declarative approach. It automatically discovers services running in Docker and configures routing and SSL certificates based on labels added to the docker-compose.yml
file.
- Configure Traefik Service: Add a new service for Traefik and an external network to the
docker-compose.yml
file.9YAMLservices: traefik: image: "traefik:v2.10" container_name: "traefik" restart: unless-stopped command: - "--api.insecure=true" - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - "--entrypoints.web.address=:80" - "--entrypoints.websecure.address=:443" - "--entrypoints.web.http.redirections.entryPoint.to=websecure" - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true" - "--certificatesresolvers.letsencrypt.acme.email=your-email@example.com" - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" ports: - "80:80" - "443:443" volumes: - "/var/run/docker.sock:/var/run/docker.sock:ro" - "traefik_acme:/letsencrypt" n8n: # existing n8n service labels: - "traefik.enable=true" - "traefik.http.routers.n8n.rule=Host(`n8n.yourdomain.com`)" - "traefik.http.routers.n8n.entrypoints=websecure" - "traefik.http.routers.n8n.tls.certresolver=letsencrypt" - "traefik.http.services.n8n.loadbalancer.server.port=5678" volumes: # existing volumes traefik_acme:
This configuration enables Traefik to listen on ports 80 and 443, automatically detect then8n
container based on its labels, obtain a Let’s Encrypt SSL certificate, and route traffic to n8n’s internal port.9
3.2 Securing Access with Authentication
After securing the external connection with a reverse proxy, it is essential to protect the n8n application itself with an authentication layer. Basic authentication is a simple and effective method for small to medium-sized deployments.2 It is configured via environment variables within the
docker-compose.yml
file.
As demonstrated in the docker-compose.yml
file in Section 2.2, the following environment variables enable basic authentication:
N8N_BASIC_AUTH_ACTIVE=true
: Activates the basic authentication feature.N8N_BASIC_AUTH_USER=admin
: Sets the username for the admin account.N8N_BASIC_AUTH_PASSWORD=StrongPasswordHere
: Sets the password.
For production deployments, it is considered a best practice to never hardcode secrets like passwords or API keys directly in a configuration file. Instead, they should be stored in a
.env
file that is referenced by docker-compose
or, for more advanced setups, in a dedicated secrets manager.
Part IV: Operational Management and Maintenance
A production-grade installation requires a clear strategy for ongoing maintenance, updates, and disaster recovery.
4.1 Updating the n8n Stack
Updating a Docker Compose-based n8n installation is a straightforward three-step process.1
- Pull the latest images: This command downloads the most recent versions of the n8n and PostgreSQL images.Bash
docker compose pull
- Stop and remove the existing containers: This shuts down the running services gracefully while preserving the persistent data in the named volumes.Bash
docker compose down
- Start the new containers: This command recreates and starts the containers with the newly pulled images.Bash
docker compose up -d
Before performing a major update, it is prudent to consult the official n8n documentation for any breaking changes that may affect the deployment.
4.2 Data Backups and Disaster Recovery
A critical distinction must be made between data persistence and data backup. The use of named Docker volumes ensures persistence, meaning the data survives container recreation. However, it does not protect against host machine failure, accidental deletion, or a compromised file system. A true disaster recovery strategy requires backing up the data off the machine.
The two essential components to back up are the n8n data volume and the PostgreSQL database.
- n8n Data Volume (
n8n_data
): This volume contains then8n
user folder, which holds critical configuration data and the encryption key. The simplest method is to use a cron job to create a compressed archive of the volume and transfer it to an external location (e.g., cloud storage or another server). - PostgreSQL Database (
postgres_data
): The database stores all workflows, credentials, and execution data. The recommended method for backing up a PostgreSQL database is to use thepg_dump
utility. This can be done by executing the command inside the runningdb
container.Bashdocker exec -t n8n_db pg_dump -U n8n n8n > n8n_db_backup.sql
This command creates a SQL dump of the database that can be transferred to a secure, external location.
Variable Name | Purpose | Best Practice Value/Recommendation |
DB_TYPE | Specifies the database type | postgresdb |
DB_POSTGRESDB_HOST | Hostname of the database service | db (the service name in docker-compose ) |
DB_POSTGRESDB_DATABASE | Database name | A strong, unique name (e.g., n8n ) |
DB_POSTGRESDB_USER | Database user | A strong, unique username (e.g., n8n ) |
DB_POSTGRESDB_PASSWORD | Database password | A strong, randomly generated password |
N8N_ENCRYPTION_KEY | Key for credential encryption | A long, randomly generated string. Back up this key! |
N8N_HOST | The host on which n8n listens | n8n.yourdomain.com |
WEBHOOK_TUNNEL_URL | The public URL for webhooks | https://n8n.yourdomain.com |
N8N_BASIC_AUTH_ACTIVE | Enables basic auth | true |
N8N_BASIC_AUTH_USER | Username for basic auth | admin (or another strong username) |
N8N_BASIC_AUTH_PASSWORD | Password for basic auth | A strong, randomly generated password |
Conclusion
A best-practice n8n installation on an Ubuntu Docker container is a multifaceted endeavor that extends beyond a simple docker run
command. It is an architectural commitment to reliability, security, and long-term maintenance. The fundamental pillars of this architecture are:
- Containerization: The use of Docker Compose to encapsulate n8n and its dependencies ensures a consistent, isolated, and easily manageable environment.
- Robust Data Layer: Shifting from the default SQLite database to a production-grade PostgreSQL instance is essential for handling concurrent operations and preserving data integrity.
- Secure Access: The deployment of a reverse proxy, such as Nginx or Traefik, is not merely a security enhancement but a functional requirement for enabling HTTPS and ensuring n8n’s webhooks and UI operate correctly.
- Proactive Security: Implementing layered security, from basic authentication to the disciplined management of sensitive environment variables and the explicit backup of the encryption key, is a non-negotiable part of responsible hosting.
By following this comprehensive guide, an administrator can establish a foundational infrastructure that is not only functional for immediate use but also scalable, secure, and resilient for mission-critical automation tasks. The principles outlined here empower the user with the knowledge to maintain the system, troubleshoot common issues, and confidently operate n8n in a production environment.
The research for the report was based on a variety of sources. Here is a consolidated list of the references used:
https://medium.com/@16priyanto/build-your-own-n8n-saas-platform-using-docker-swarm-traefik-65193b89c0ba
https://docs.n8n.io/hosting/installation/docker/
https://www.digitalocean.com/community/tutorials/how-to-setup-n8n
https://docs.n8n.io/hosting/configuration/environment-variables/deployment/
https://hub.docker.com/r/n8nio/n8n
https://github.com/n8n-io/n8n-hosting/tree/main/docker-compose/withPostgres
https://docs.docker.com/engine/security/rootless/
https://www.thomas-krenn.com/en/wiki/Installation_of_Reverse_Proxy_for_n8n_with_Nginx_Proxy_Manager
https://www.reddit.com/r/n8n/comments/1k47ats/n8n_best_practices_for_clean_profitable/
https://osher.com.au/blog/guide-to-n8n-configuration-settings/
https://dev.to/tahsin000/how-i-deploy-n8n-on-a-vps-with-docker-nginx-https-simple-step-by-step-2g73
https://jortdevreeze.com/blog/ai-agents-6/how-to-install-n8n-with-docker-and-traefik-32
https://community.n8n.io/t/self-hosted-n8n-docker-with-nginx-for-ssl/167459
https://groovetechnology.com/blog/software-development/comprehensive-guide-to-n8n-installation-set-up-for-success/
https://flywp.com/blog/13138/deploy-n8n-workflow-automation-with-docker-compose/
https://www.youstable.com/blog/install-n8n-on-docker/
https://www.youtube.com/watch?v=a_JpkazmcoI
https://runcloud.io/blog/n8n-hosting-docker-nginx
https://www.hostinger.com/tutorials/how-to-self-host-n8n-with-docker
https://www.reddit.com/r/n8n/comments/1mfotxf/whats_your_approach_for_managing_environment/