Sharcon AI Automation CompanySHARCON
Back to Blog
n8n
docker
self-hosting
workflow automation
vps

How to Self-Host n8n with Docker on a VPS

November 6, 2026

Learn how to self-host n8n on a VPS using Docker Compose, Caddy HTTPS, persistent storage, and secure encryption keys. This step-by-step guide covers installation, configuration, backups, updates, webhook testing, and troubleshooting.

How to Self-Host n8n with Docker on a VPS

To self-host n8n with Docker, you need a Linux VPS, Docker Engine, Docker Compose, a persistent data volume, and a reverse proxy that provides HTTPS. The recommended setup runs n8n inside a Docker container, stores its data outside the container, and exposes the application through a domain secured with SSL/TLS.

This guide walks through a complete n8n Docker Compose deployment on Ubuntu, including HTTPS with Caddy, environment variables, encryption keys, persistent storage, backups, updates, and troubleshooting. By the end, you'll have an n8n instance accessible through your own domain without relying on n8n Cloud.

What You Need Before Self-Hosting n8n

n8n is a workflow automation platform that connects APIs, databases, CRMs, webhooks, and AI services. Running it yourself gives you control over the infrastructure, data storage, deployment schedule, and integrations.

For this tutorial, we'll use the following stack:

ComponentConfigurationPurpose
VPSUbuntu 24.04 LTSHosts the application
Docker EngineCurrent stable releaseRuns containers
Docker ComposeDocker Compose v2Manages services
n8nOfficial Docker imageExecutes automation workflows
DatabaseSQLiteStores workflows, credentials, and execution metadata
Reverse proxyCaddyHandles HTTPS and incoming requests
Domainautomation.example.comProvides a stable public URL
Persistent storageDocker named volumePreserves n8n data across container replacements

VPS requirements

For a small deployment, a reasonable starting configuration is:

  • 2 vCPUs
  • 4 GB RAM
  • 30–40 GB SSD storage
  • Ubuntu 24.04 LTS
  • A public IPv4 address
  • SSH access with sudo privileges

These are practical starting specifications, not official minimum requirements or performance guarantees. Resource usage depends on workflow concurrency, execution history, file processing, and the services your workflows call.

A 2 GB VPS may work for light experimentation. For production workflows involving AI APIs, CRM synchronization, scheduled jobs, or multiple simultaneous executions, 4 GB provides more breathing room.

SQLite is sufficient for getting started and for many modest workloads. PostgreSQL is worth considering when operational requirements become more demanding, particularly if you expect to scale beyond a single n8n process.

Before starting: Create a DNS A record pointing automation.example.com to your VPS public IP address. Replace this example hostname with your actual domain throughout the tutorial.

Also confirm that your VPS provider allows inbound TCP traffic on ports 80 and 443.

Step 1: Connect to Your VPS and Update Ubuntu

Connect using SSH:

ssh ubuntu@YOUR_SERVER_IP

Replace ubuntu with your VPS username.

Update installed packages:

sudo apt update && sudo apt upgrade -y

Install basic utilities:

sudo apt install -y ca-certificates curl gnupg openssl

Check your operating system:

cat /etc/os-release

The commands below assume Ubuntu 24.04 LTS. Other Linux distributions may require different package installation commands.

For a production server, use SSH key authentication, disable unnecessary services, and keep the operating system updated.

Step 2: Install Docker Engine and Docker Compose

Install Docker from Docker's official Ubuntu package repository rather than relying on an outdated package supplied by the operating system.

First, remove potentially conflicting packages:

sudo apt remove -y docker.io docker-compose docker-compose-v2 docker-doc podman-docker containerd runc

It's normal if some packages are not installed.

Create the Docker repository key directory:

sudo install -m 0755 -d /etc/apt/keyrings

Download Docker's GPG key:

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 the official repository:

sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
EOF

Install Docker and the Compose plugin:

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Verify the installation:

sudo docker --version
sudo docker compose version

Test Docker:

sudo docker run --rm hello-world

You should see a message confirming that Docker can download and run containers.

Security note: This tutorial uses sudo docker because membership in the Docker group effectively grants root-level control over the host. Avoid adding users to that group unless you understand the implications.

Step 3: Configure Your VPS Firewall

The deployment will expose only SSH, HTTP, and HTTPS publicly.

If you use UFW, allow SSH before enabling the firewall:

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

Verify the rules:

sudo ufw status

Expected allowed ports:

PortProtocolPurpose
22TCPSSH administration
80TCPHTTP and certificate validation
443TCPHTTPS access

You do not need to open port 5678 publicly.

n8n listens on port 5678 inside its Docker network, while Caddy receives external requests on ports 80 and 443.

This separation reduces unnecessary exposure of the application.

Docker firewall caveat: Docker-published ports can bypass some UFW filtering behavior. The Compose configuration below publishes only Caddy's ports 80 and 443. The n8n service uses expose, not ports, so its application port is not published on the host.

If your VPS provider offers a network firewall, apply equivalent inbound restrictions there as well.

Step 4: Create Your n8n Docker Compose Project

Create a dedicated directory:

sudo mkdir -p /opt/n8n
sudo chown "$USER":"$USER" /opt/n8n
cd /opt/n8n

We'll keep three configuration files here:

/opt/n8n/
├── compose.yaml
├── .env
└── Caddyfile

The .env file stores configuration values and secrets. The Compose file defines the containers. The Caddyfile controls HTTPS and reverse proxy behavior.

Generate your n8n encryption key

n8n encrypts saved credentials before storing them in its database.

Generate a random encryption key:

openssl rand -hex 32

This produces a 64-character hexadecimal string representing 32 random bytes.

Example format:

8f2c...your-generated-64-character-key...a91d

Use the actual complete output from your command, not the abbreviated example.

Keep this key permanently. Losing it can make previously saved credentials unreadable, even if you still have the database.

Create the environment file

Run:

nano .env

Add:

N8N_HOST=automation.example.com
N8N_PROTOCOL=https
WEBHOOK_URL=https://automation.example.com/
N8N_EDITOR_BASE_URL=https://automation.example.com/
N8N_ENCRYPTION_KEY=REPLACE_WITH_YOUR_GENERATED_KEY
GENERIC_TIMEZONE=America/New_York
TZ=America/New_York

Replace the hostname and encryption key.

The timezone values above are examples. Use the appropriate IANA timezone for your deployment, such as Europe/London or America/Chicago.

Protect the file:

chmod 600 .env

Keep .env out of public repositories and shared backups that are not encrypted.

Understanding the environment variables

VariablePurpose
N8N_HOSTPublic hostname for n8n
N8N_PROTOCOLIndicates HTTPS is used externally
WEBHOOK_URLPublic base URL for webhook endpoints
N8N_EDITOR_BASE_URLPublic editor URL
N8N_ENCRYPTION_KEYEncrypts stored credentials
GENERIC_TIMEZONEDefault timezone for workflow scheduling
TZSets the container's system timezone

Two additional settings will be defined directly in Docker Compose:

  • N8N_PORT=5678 keeps n8n listening on its internal application port.
  • N8N_PROXY_HOPS=1 tells n8n to trust forwarded proxy information from one reverse proxy.

Setting N8N_PROTOCOL=https does not make the n8n container serve HTTPS directly. Caddy terminates TLS and forwards requests to n8n over the private Docker network.

Step 5: Create the n8n Docker Compose File

Create the Compose configuration:

nano compose.yaml

Paste the following:

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n:stable
    restart: unless-stopped
    environment:
      - N8N_HOST=${N8N_HOST}
      - N8N_PORT=5678
      - N8N_PROTOCOL=${N8N_PROTOCOL}
      - WEBHOOK_URL=${WEBHOOK_URL}
      - N8N_EDITOR_BASE_URL=${N8N_EDITOR_BASE_URL}
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
      - N8N_PROXY_HOPS=1
      - GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
      - TZ=${TZ}
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
    volumes:
      - n8n_data:/home/node/.n8n
    expose:
      - "5678"
    networks:
      - automation

  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      - n8n
    networks:
      - automation

volumes:
  n8n_data:
  caddy_data:
  caddy_config:

networks:
  automation:
    driver: bridge

Why this configuration works

Persistent n8n storage:

volumes:
  - n8n_data:/home/node/.n8n

This stores n8n's application data in a Docker-managed volume instead of the container's writable filesystem.

For the default SQLite setup, this location contains the database and other important application files.

Removing and recreating the container does not remove the named volume unless you explicitly delete it.

No public n8n port:

expose:
  - "5678"

The expose directive documents the internal port without publishing it to the VPS network interfaces.

Caddy reaches n8n through Docker's internal DNS using the service name n8n.

Automatic container restart:

restart: unless-stopped

Docker restarts the service after failures or host reboots, unless it was explicitly stopped.

Image versioning:

The stable tag is convenient for an initial installation, but it is mutable. For production deployments, replace it with a tested, explicit n8n version after verifying compatibility with your workflows and integrations.

Avoid automatically upgrading n8n without a backup and rollback plan. Database migrations can make rolling back more complicated than simply restoring an older container image.

Step 6: Configure HTTPS with Caddy

n8n integrations frequently depend on webhooks, OAuth callbacks, and external API requests.

A stable HTTPS URL is therefore a practical requirement for most production deployments.

Caddy provides automatic TLS certificate management when your domain resolves correctly and the required ports are reachable.

Create the configuration:

nano Caddyfile

Add:

automation.example.com {
    encode zstd gzip

    reverse_proxy n8n:5678
}

Replace automation.example.com with your real hostname.

Caddy will:

  1. Accept requests for your domain.
  2. Obtain and renew a trusted TLS certificate when the domain and network configuration permit it.
  3. Forward requests to the n8n container.
  4. Preserve the information n8n needs to understand the original request.

Caddy's reverse proxy handles WebSocket upgrades, which n8n can use for editor functionality.

Verify your DNS configuration

From the VPS, run:

getent ahostsv4 automation.example.com

The returned address should include your VPS public IPv4 address.

If the hostname has an AAAA record, ensure IPv6 also points to a reachable server configured to accept the relevant traffic. Otherwise, remove the incorrect AAAA record.

If you use a DNS proxy or CDN, certificate issuance and request routing may require additional configuration.

For the simplest initial deployment, point the hostname directly to the VPS.

Step 7: Start Your Self-Hosted n8n Instance

From /opt/n8n, validate the Compose configuration:

sudo docker compose config --quiet

If the command exits successfully, start the containers:

sudo docker compose up -d

Docker downloads the required images and starts n8n and Caddy.

Check their status:

sudo docker compose ps

You should see both services running.

Inspect n8n startup logs:

sudo docker compose logs --tail=100 n8n

Inspect Caddy logs:

sudo docker compose logs --tail=100 caddy

Once DNS and HTTPS are working, open:

https://automation.example.com

You should reach the n8n setup screen.

Create the initial owner account and complete any setup prompts presented by your installed n8n version.

Use a strong, unique password and enable additional authentication protections supported by your deployment.

Your self-hosted n8n instance is now running.

Step 8: Test Webhooks and Persistent Storage

A successful login confirms that the editor works, but it does not prove that workflows and incoming webhooks are configured correctly.

Test both before using the instance for business-critical automations.

Test a webhook

Inside n8n:

  1. Create a new workflow.
  2. Add a Webhook trigger node.
  3. Set its HTTP method to GET.
  4. Set the path to docker-test.
  5. Configure the response behavior so the webhook returns a response when called.
  6. Save the workflow.
  7. Activate or publish it using the control provided by your n8n version.

The production webhook URL should resemble:

https://automation.example.com/webhook/docker-test

Test it:

curl -i https://automation.example.com/webhook/docker-test

A successful request should reach your workflow and produce a response.

The exact response depends on the webhook node's response settings.

Production versus test webhooks: n8n distinguishes between test webhook URLs and production webhook URLs. Test URLs are generally available while the editor is listening for a test event. Production URLs require the workflow to be active or published.

If a test URL works but the production URL does not, verify the workflow's activation state before changing your reverse proxy.

Test persistent storage

Create a simple workflow and save it.

Then restart the n8n container:

sudo docker compose restart n8n

Refresh the editor and confirm that the workflow still exists.

You can also recreate the container:

sudo docker compose up -d --force-recreate n8n

The saved workflow should remain available because the database lives in the persistent Docker volume.

This verifies basic persistence, but it is not a substitute for testing backups.

Step 9: Back Up n8n Before Running Production Workflows

A self-hosted n8n installation contains more than workflow definitions.

Important data can include:

  • Workflow definitions and settings
  • Encrypted API credentials
  • Execution history
  • Application configuration
  • Local binary data, depending on configuration
  • The encryption key required to decrypt credentials

For the SQLite configuration in this guide, the persistent n8n volume is the primary application data source.

Create a consistent volume backup

Stop n8n temporarily to avoid copying SQLite database files while they are being modified:

cd /opt/n8n
sudo docker compose stop n8n

Create a backup directory:

sudo mkdir -p /opt/n8n-backups

Back up the named volume:

sudo docker run --rm \
  -v n8n_n8n_data:/source:ro \
  -v /opt/n8n-backups:/backup \
  alpine \
  tar czf /backup/n8n-data-$(date +%F-%H%M%S).tar.gz -C /source .

This assumes your Compose project is named n8n, which is the default when running from /opt/n8n without an overridden project name.

Confirm the actual volume name if necessary:

sudo docker volume ls

Restart n8n:

sudo docker compose start n8n

Back up the configuration files separately:

sudo tar czf /opt/n8n-backups/n8n-config-$(date +%F-%H%M%S).tar.gz \
  -C /opt/n8n compose.yaml .env Caddyfile

Because this archive contains your encryption key, treat it as sensitive secret material.

Encrypt backups before storing them outside the VPS, restrict access, and use a backup destination that is separate from the production server.

Critical detail: If you restore the database without the original N8N_ENCRYPTION_KEY, saved credentials may no longer be decryptable.

Recovery checklist

Before relying on backups, verify that you can:

  1. Retrieve the backup from storage outside the VPS.
  2. Restore the n8n data volume onto a clean test deployment.
  3. Restore the matching encryption key.
  4. Start a compatible n8n version.
  5. Open saved workflows and confirm that stored credentials remain usable.
  6. Confirm that webhooks and external integrations reconnect correctly.

A backup that has never been restored is an unverified recovery plan.

For organizations running customer-facing business automation workflows, recovery planning should be part of the deployment rather than an afterthought.

Step 10: Update n8n Safely with Docker Compose

Self-hosting means you are responsible for installing security patches, reviewing version changes, and maintaining compatibility.

Before upgrading:

  1. Review the n8n release notes for breaking changes.
  2. Back up the database, encryption key, and configuration.
  3. Record the currently deployed image version.
  4. Test major upgrades in a separate environment when possible.
  5. Schedule maintenance if workflows cannot tolerate interruptions.

To download the image configured in your Compose file:

cd /opt/n8n
sudo docker compose pull n8n

Recreate n8n using that image:

sudo docker compose up -d --no-deps n8n

Check startup logs:

sudo docker compose logs --tail=100 n8n

Verify that:

  • The editor loads.
  • Existing workflows are present.
  • Stored credentials remain usable.
  • Production webhooks respond correctly.
  • Scheduled workflows execute as expected.

When using an explicit image version, update the version in compose.yaml before pulling.

Do not assume downgrading the Docker image will reverse database migrations. If an upgrade changes the database schema, recovery may require restoring the database backup created before the upgrade.

Caddy should also receive periodic security updates, but update and verify it separately when practical.

n8n Encryption Key Docker Setup: What Can Go Wrong?

The encryption key is one of the most consequential settings in a self-hosted deployment.

n8n uses it to protect credentials such as API tokens, OAuth credentials, and database passwords stored by the application.

The setting is:

N8N_ENCRYPTION_KEY=YOUR_PERSISTENT_SECRET

What happens if you do not set an encryption key?

n8n can generate an encryption key and store it in its configuration directory.

That behavior can work when the configuration directory is persistent, but explicitly setting N8N_ENCRYPTION_KEY makes the secret easier to manage through a controlled deployment process.

It also reduces the risk of accidentally relying on a key stored only inside an ephemeral container.

Can you change the encryption key later?

Do not casually replace the encryption key on an existing installation.

Credentials encrypted with the previous key generally cannot be decrypted using a different one.

Changing the environment variable alone does not re-encrypt existing credentials.

If key rotation is required, follow a migration procedure compatible with your installed n8n version. Preserve the original key and a verified backup until the migration is complete.

Should the key be stored in Docker Compose?

Avoid placing secret values directly inside compose.yaml, especially if that file is tracked in Git.

The .env approach shown here keeps the value separate from the Compose definition, although it is not a full secret-management solution.

Docker Compose substitutes environment variables into the container configuration. Users with sufficient Docker or host privileges can potentially inspect those values.

For deployments with stronger compliance or security requirements, use a suitable secrets-management system and document how the application receives its key.

Common n8n Docker Errors and How to Fix Them

Most failed VPS deployments come down to networking, file permissions, missing environment variables, or persistence issues.

Error: 502 Bad Gateway

A 502 response from Caddy usually indicates that the reverse proxy cannot successfully connect to n8n.

Check container status:

sudo docker compose ps

Then inspect logs:

sudo docker compose logs --tail=100 n8n
sudo docker compose logs --tail=100 caddy

Verify that the Caddyfile contains:

reverse_proxy n8n:5678

Do not use localhost:5678 in Caddy's container configuration. Inside the Caddy container, localhost refers to Caddy itself, not the n8n container.

Also confirm that both containers belong to the same Docker network.

Error: HTTPS certificate not issued

Check that your domain points to the correct VPS:

getent ahostsv4 automation.example.com

Confirm that ports 80 and 443 are reachable from the internet and that another web server is not occupying them.

Check Caddy logs:

sudo docker compose logs caddy

Common causes include incorrect DNS records, blocked ports, and conflicting reverse proxies.

Certificate issuance can also fail because of certificate authority validation errors or rate limits. The Caddy logs should provide more specific information.

Error: Webhooks show the wrong URL

Check these values in .env:

N8N_HOST=automation.example.com
N8N_PROTOCOL=https
WEBHOOK_URL=https://automation.example.com/
N8N_EDITOR_BASE_URL=https://automation.example.com/

Confirm that Compose also sets:

- N8N_PROXY_HOPS=1

After changing .env, recreate n8n:

sudo docker compose up -d --no-deps --force-recreate n8n

A regular container restart does not apply changed Compose environment variables to an existing container.

Error: Permission denied in /home/node/.n8n

The official n8n container runs the application using a non-root user.

Problems can occur when a host directory mounted into /home/node/.n8n has incompatible ownership.

The named-volume configuration in this tutorial avoids many common bind-mount permission problems.

If you switch to a bind mount, ensure that the directory is writable by the container's application user. Do not make the directory world-writable as a shortcut.

Inspect the exact error before changing ownership or permissions.

Error: Credentials cannot be decrypted

This commonly indicates that the application is using a different encryption key from the one used to encrypt existing credentials.

Check whether the original .env was restored and whether the key was changed during deployment.

Do not generate a replacement key in an attempt to repair existing encrypted credentials.

Restore the correct key from a secure backup.

Error: Workflows disappear after recreating containers

Check whether /home/node/.n8n is attached to persistent storage.

Inspect the mounted volumes:

sudo docker inspect $(sudo docker compose ps -q n8n) --format '{{json .Mounts}}'

The output should show a mount targeting /home/node/.n8n.

Also check whether you accidentally started the application under a different Compose project name, which can create a separate named volume.

Avoid running:

sudo docker compose down -v

The -v option removes Compose-managed named volumes, including the volume containing your n8n data.

Mistakes to Avoid When Self-Hosting n8n

1. Publishing port 5678 directly to the internet

A configuration such as:

ports:
  - "5678:5678"

publishes n8n on the VPS network interfaces by default.

For this architecture, that is unnecessary. Let Caddy handle public requests over HTTPS while n8n remains accessible only inside the Docker network.

2. Forgetting persistent storage

Containers are replaceable. Application data must survive container replacement.

Always mount persistent storage at /home/node/.n8n and verify that workflows survive recreation.

3. Losing the encryption key

Backing up the database without preserving the matching encryption key can leave credentials unusable after restoration.

Store the key securely and include it in your recovery documentation.

4. Treating HTTPS as complete application security

TLS protects traffic between clients and the server, but it does not protect against every application vulnerability, compromised credential, or malicious workflow.

Keep n8n updated, restrict administrative access, protect server credentials, and review who can create or modify workflows.

5. Automatically upgrading without testing

Workflow behavior can change between n8n versions, particularly when nodes, authentication mechanisms, or integrations change.

Pin tested versions for production systems and review release notes before upgrading.

6. Keeping backups on the same VPS

A backup stored only on the production disk may be lost during the same server failure or compromise.

Keep encrypted copies in independent storage and periodically test restoration.

7. Ignoring execution history growth

Execution records can increase database size over time.

Review your n8n version's execution-data retention and pruning settings. Configure retention based on debugging needs, privacy requirements, and available storage.

Monitor disk usage:

df -h
sudo docker system df

Do not indiscriminately delete Docker volumes to recover space.

8. Assuming a single VPS provides high availability

A single VPS remains a single point of failure.

Automatic container restarts help recover from individual process failures, but they do not protect against VPS outages, disk corruption, or provider-level incidents.

For critical automations, define acceptable downtime, backup frequency, and recovery procedures before going live.

Should You Use SQLite or PostgreSQL for n8n?

The deployment above uses SQLite because it keeps the initial setup manageable.

For a single n8n instance handling a modest workload, SQLite can be a reasonable choice.

However, PostgreSQL provides a more flexible foundation for deployments with growing operational requirements.

ConsiderationSQLitePostgreSQL
Initial setupSimplerRequires a database service
Separate database serverNot requiredSupported
Database backup optionsFile-based or SQLite-aware backupPostgreSQL backup and recovery tools
Operational complexityLowerHigher
Scaling optionsMore limitedBetter suited to advanced architectures
Best fitSmall single-instance deploymentsLarger or more demanding deployments

For queue-mode architectures and distributed execution, consult the current n8n documentation for supported database and infrastructure requirements. Do not assume a SQLite deployment can be expanded simply by adding more worker containers.

If your automations synchronize customer records across HubSpot, Zoho, or other platforms, database selection should reflect execution volume, reliability requirements, and how much downtime the business can tolerate.

The same applies to CRM integrations that depend on incoming webhooks and consistent credential availability.

Production Readiness Checklist for Your n8n VPS

Before connecting real customer data or business-critical APIs, verify the following:

CheckExpected result
DNSDomain resolves to the intended VPS
HTTPSValid certificate and successful HTTPS connection
Network exposureOnly required ports are publicly reachable
n8n accessOwner account created and protected
Persistent storageWorkflows survive container recreation
Encryption keyExplicitly configured and securely backed up
WebhooksProduction webhook endpoint responds correctly
TimezoneMatches workflow scheduling requirements
BackupsDatabase, encryption key, and config are recoverable
Restore testSuccessful restoration in a separate environment
UpdatesVersioning and maintenance process documented
MonitoringContainer failures and disk usage can be detected
Access controlSSH and administrative access are restricted
RecoveryServer replacement procedure is documented

A simple monthly maintenance routine

For a small deployment, establish a recurring maintenance process rather than waiting for something to fail.

Every week: Check disk usage, container status, failed workflows, and whether backups completed successfully.

Every month: Review n8n and Caddy security updates, inspect execution-data growth, and confirm that unnecessary integrations and credentials have been removed.

Periodically: Restore a backup in a separate environment and verify that workflows and credentials work as expected.

The appropriate maintenance frequency depends on business impact. A workflow that sends internal notifications has different recovery requirements from one that handles customer appointments or payment-related events.

When Self-Hosting n8n Makes Sense

Self-hosting is a good fit when you want control over infrastructure, need custom networking, or expect to connect n8n to private systems and internal APIs.

It is also useful when workflows require specialized dependencies or deployment configurations that are easier to manage on your own server.

However, self-hosting does not eliminate operating costs. You still pay for VPS resources, backups, monitoring, administration, and any third-party APIs used by your workflows.

AI model usage, SMS messages, telephony, email delivery, and external SaaS subscriptions are generally separate from the cost of running n8n itself.

For example, an automation that receives a lead through a webhook, validates the data, creates a CRM contact, and sends a notification may involve charges from the CRM, messaging provider, or other connected services.

Self-hosting n8n controls where the orchestration runs. It does not make external services free.

Also review n8n's current licensing terms before offering hosted automation services to third parties or embedding n8n into a commercial product. Self-hosting does not automatically grant unrestricted rights for every commercial use case.

FAQ: Self-Hosting n8n with Docker

1. What is the easiest way to self-host n8n with Docker Compose?

The simplest persistent setup uses the official n8n Docker image, a Docker named volume mounted at /home/node/.n8n, and a Compose file that defines the container's environment variables. For a publicly accessible installation, add a reverse proxy such as Caddy to provide HTTPS and route requests to n8n's internal port 5678. The Compose configuration in this guide includes all three components.

2. Can I run n8n on a VPS with 2 GB RAM?

Yes, a 2 GB VPS can be sufficient for experimentation and light workflows, depending on their memory requirements and concurrency. For a production deployment, 4 GB RAM is a more comfortable starting point, especially when executing multiple workflows or processing larger payloads. Monitor actual memory consumption before deciding whether the server needs more resources.

3. How do I set the n8n encryption key in Docker?

Generate a random key with openssl rand -hex 32, save it as N8N_ENCRYPTION_KEY in your .env file, and reference that variable in Docker Compose. Keep the value unchanged for the lifetime of the associated encrypted credentials unless you perform a supported key migration. Back up the encryption key separately and securely alongside your database recovery materials.

4. How do I update a self-hosted n8n Docker installation without losing workflows?

Store n8n data in a persistent volume, create a verified backup, and update the configured Docker image using docker compose pull n8n followed by docker compose up -d --no-deps n8n. Recreating the container preserves the named volume. However, updates may perform database migrations, so review release notes and retain a compatible pre-upgrade backup.

5. Does n8n Docker Compose require PostgreSQL?

No. n8n supports SQLite, which is the default database used by the configuration in this tutorial. PostgreSQL is an option for deployments that need more advanced database operations or scaling architectures. Choose the database based on workload, recovery requirements, and the deployment model rather than assuming every VPS installation needs PostgreSQL immediately.

Next Steps: Build Reliable Automations on Your VPS

A working n8n Docker deployment provides the infrastructure, but dependable automation also requires workflow error handling, API authentication, webhook validation, and monitoring.

Once your instance is running, start with one workflow, verify its failure behavior, and document how to recover it before adding more integrations.

If you need help connecting your self-hosted n8n instance to CRMs, APIs, or AI services, explore Sharcon's AI automation services.

Max Lebedev

Max Lebedev

CEO of Sharcon LLC

15+ years in marketing and development. Leading a team of 30+ professionals at Sharcon.