<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tech Workflow Hub</title>
    <description>The latest articles on DEV Community by Tech Workflow Hub (@techworkflowhub).</description>
    <link>https://dev.to/techworkflowhub</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4245138%2F68dcf0af-d349-4182-9dad-6bc0aa96d918.png</url>
      <title>DEV Community: Tech Workflow Hub</title>
      <link>https://dev.to/techworkflowhub</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/techworkflowhub"/>
    <language>en</language>
    <item>
      <title>How to Self-Host n8n with Docker Compose and PostgreSQL (2026 Architecture Guide)</title>
      <dc:creator>Tech Workflow Hub</dc:creator>
      <pubDate>Sun, 15 Nov 2026 15:47:45 +0000</pubDate>
      <link>https://dev.to/techworkflowhub/how-to-self-host-n8n-with-docker-compose-and-postgresql-2026-architecture-guide-54l1</link>
      <guid>https://dev.to/techworkflowhub/how-to-self-host-n8n-with-docker-compose-and-postgresql-2026-architecture-guide-54l1</guid>
      <description>&lt;p&gt;When you outgrow 2,500 monthly executions on managed workflow clouds—or run polling and AI loops that burn per-operation credits—self-hosting &lt;strong&gt;n8n Community Edition&lt;/strong&gt; on a single Linux VPS gives you unlimited workflow runs for a flat server bill ($10–$20/month).&lt;/p&gt;

&lt;p&gt;This guide walks through a clean, production-safe Docker Compose setup backed by &lt;strong&gt;PostgreSQL&lt;/strong&gt; (instead of the default SQLite file) and &lt;strong&gt;Caddy&lt;/strong&gt; for automatic HTTPS.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Why PostgreSQL + Reverse Proxy Matter in Production
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Avoid SQLite locking:&lt;/strong&gt; n8n's default SQLite database works for quick local tests, but concurrent webhook bursts cause write locks. PostgreSQL handles parallel executions and retains clean execution logs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bind n8n to 127.0.0.1:&lt;/strong&gt; Never expose port 5678 directly to the public internet. Bind it to localhost and terminate TLS via Caddy or Nginx.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pin N8N_ENCRYPTION_KEY:&lt;/strong&gt; n8n encrypts all API credentials stored in its database using this key. Losing it locks you out of every saved credential on restore.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Server Prerequisites (Ubuntu 24.04 LTS)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;2 vCPU / 2–4 GB RAM VPS (enough for most small-team automations)&lt;/li&gt;
&lt;li&gt;Docker Engine + Docker Compose plugin installed&lt;/li&gt;
&lt;li&gt;A domain or subdomain (
8n.yourdomain.com) with an A record pointing to your VPS IP&lt;/li&gt;
&lt;li&gt;Ports 80 and 443 open in your firewall (ufw allow 80,443/tcp)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Generate Secrets &amp;amp; .env
&lt;/h2&gt;

&lt;p&gt;Create a project directory /opt/n8n and generate strong secrets before starting containers:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ash&lt;br&gt;
mkdir -p /opt/n8n &amp;amp;&amp;amp; cd /opt/n8n&lt;br&gt;
openssl rand -hex 32   # Use for N8N_ENCRYPTION_KEY&lt;br&gt;
openssl rand -hex 20   # Use for POSTGRES_PASSWORD&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Save /opt/n8n/.env:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;env&lt;br&gt;
N8N_DOMAIN=n8n.yourdomain.com&lt;br&gt;
POSTGRES_PASSWORD=your_generated_postgres_password&lt;br&gt;
N8N_ENCRYPTION_KEY=your_generated_32_byte_hex_key&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Docker Compose Configuration (docker-compose.yml)
&lt;/h2&gt;

&lt;p&gt;`yaml&lt;br&gt;
services:&lt;br&gt;
  postgres:&lt;br&gt;
    image: postgres:16-alpine&lt;br&gt;
    restart: unless-stopped&lt;br&gt;
    environment:&lt;br&gt;
      POSTGRES_USER: n8n&lt;br&gt;
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}&lt;br&gt;
      POSTGRES_DB: n8n&lt;br&gt;
    volumes:&lt;br&gt;
      - pg_data:/var/lib/postgresql/data&lt;br&gt;
    healthcheck:&lt;br&gt;
      test: ["CMD-SHELL", "pg_isready -U n8n -d n8n"]&lt;br&gt;
      interval: 10s&lt;br&gt;
      timeout: 5s&lt;br&gt;
      retries: 5&lt;/p&gt;

&lt;p&gt;n8n:&lt;br&gt;
    image: docker.n8n.io/n8nio/n8n:latest&lt;br&gt;
    restart: unless-stopped&lt;br&gt;
    depends_on:&lt;br&gt;
      postgres:&lt;br&gt;
        condition: service_healthy&lt;br&gt;
    ports:&lt;br&gt;
      - "127.0.0.1:5678:5678"&lt;br&gt;
    environment:&lt;br&gt;
      DB_TYPE: postgresdb&lt;br&gt;
      DB_POSTGRESDB_HOST: postgres&lt;br&gt;
      DB_POSTGRESDB_PORT: 5432&lt;br&gt;
      DB_POSTGRESDB_DATABASE: n8n&lt;br&gt;
      DB_POSTGRESDB_USER: n8n&lt;br&gt;
      DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}&lt;br&gt;
      N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}&lt;br&gt;
      N8N_HOST: ${N8N_DOMAIN}&lt;br&gt;
      N8N_PROTOCOL: https&lt;br&gt;
      WEBHOOK_URL: https://${N8N_DOMAIN}/&lt;br&gt;
      GENERIC_TIMEZONE: UTC&lt;br&gt;
    volumes:&lt;br&gt;
      - n8n_data:/home/node/.n8n&lt;/p&gt;

&lt;p&gt;volumes:&lt;br&gt;
  pg_data:&lt;br&gt;
  n8n_data:&lt;br&gt;
`&lt;/p&gt;

&lt;p&gt;Start the stack with docker compose up -d.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Automatic HTTPS via Caddy (/etc/caddy/Caddyfile)
&lt;/h2&gt;

&lt;p&gt;Install Caddy on the host and point it to 127.0.0.1:5678:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;caddy&lt;br&gt;
n8n.yourdomain.com {&lt;br&gt;
    reverse_proxy 127.0.0.1:5678&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Reload Caddy (systemctl reload caddy) and your instance will automatically provision a Let's Encrypt certificate so OAuth callbacks and webhooks resolve over HTTPS.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Essential Backup Routine &amp;amp; Trade-Offs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Daily automated backup:&lt;/strong&gt; Run pg_dump via cron (docker compose exec -T postgres pg_dump -U n8n n8n &amp;gt; backup.sql) and back up /opt/n8n/.env separately in an encrypted password vault.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who should skip self-hosting:&lt;/strong&gt; While the Community Edition software license is free with unlimited executions, it excludes enterprise SAML/LDAP SSO, multiple environments, and managed uptime. If you have zero Linux/Docker admin bandwidth and run fewer than 2,500 workflows per month, managed cloud tiers remain simpler.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>n8n</category>
      <category>docker</category>
      <category>automation</category>
      <category>selfhosting</category>
    </item>
  </channel>
</rss>
