Deploy n8n on Alibaba Cloud Linux 3 with Docker

Devops & Infrastructure, Open Source, Tips & Tricks, and Tutorials

Deploy n8n on Alibaba Cloud Linux 3 with Docker

n8n is an open-source workflow automation tool — think Zapier, but self-hosted, fair-code-licensed, and with code-level escape hatches when the visual builder runs out of road. Running it on your own VPS means you keep your data, dodge per-execution pricing, and can hit internal APIs that public SaaS workflow tools cannot reach.

This guide walks through deploying n8n on an Alibaba Cloud ECS instance using Docker — from spinning up the VPS, through Docker Compose with PostgreSQL, to TLS via Nginx and Let's Encrypt, and finally automated image updates. We will also cover the gotchas that bite production n8n installs: the WEBHOOK_URL env var, basic auth, time zones, and a sane Watchtower interval.

If you are deploying to a different provider, the Docker steps are identical — only the ECS console clicks change. The same workflow applies on Vultr, Hetzner, DigitalOcean, or any other cloud running Alibaba Cloud Linux 3, Ubuntu, or Debian. For a generic-VPS walkthrough that skips the Aliyun-specific provisioning and adds production hardening (queue mode, continuous deployment via git push, off-host backups), see our self-host n8n on a VPS with Docker guide instead.

Why Docker (and why not bare metal)

n8n ships as a Node.js application, but the official n8nio/n8n image bundles every native dependency, the correct Node version, and a pinned task runner. Running bare metal means you own Node upgrades, native build tools, and the chmod dance for the ~/.n8n config directory. Docker means a single docker compose pull && docker compose up -d to roll forward.

Docker also matches how DeployHQ handles container deployments: build images in the build pipeline, push to a registry, then trigger a docker compose pull on the VPS via SSH commands. No manual SSH sessions, no version drift between staging and production.

Prerequisites

  • Alibaba Cloud account with billing enabled
  • A domain name pointed at your future ECS public IP (A record)
  • Local SSH client and a key pair you do not mind uploading to Alibaba Cloud
  • Comfort with the Linux command line and a working knowledge of Docker

Step 1: Create an Alibaba Cloud ECS instance

  1. Log in to the Alibaba Cloud Console and open Elastic Compute Service (ECS).
  2. Click Create Instance and pick a region close to your users — latency to the n8n editor matters more than you think.
  3. Recommended specs for a small-to-medium n8n instance:
    • OS: Alibaba Cloud Linux 3 or Ubuntu 22.04 LTS
    • CPU/RAM: 2 vCPU / 4 GB (n8n itself is light, but Chromium-based community nodes are not)
    • Disk: 50 GB SSD — workflow execution history grows fast
  4. Configure the security group to allow:
    • SSH (22) from your IP only — never 0.0.0.0/0 in production
    • HTTP (80) — needed for the Let's Encrypt HTTP-01 challenge
    • HTTPS (443) — your only public-facing n8n port
    • Do not expose port 5678 publicly. Nginx will proxy to it on localhost.

Step 2: Install Docker on Alibaba Cloud Linux 3

The default Alibaba Cloud Linux 3 image ships without Docker. Skip the docker.io apt package — it lags multiple versions behind upstream. Use Docker's official repository instead:

# Update the system
sudo dnf update -y

# Install dependencies
sudo dnf install -y dnf-plugins-core

# Add Docker's official repository (CentOS 8 repo works on Alibaba Cloud Linux 3)
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# Install Docker Engine and the Compose plugin
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Enable and start Docker
sudo systemctl enable --now docker

# Add your user to the docker group (log out and back in for it to take effect)
sudo usermod -aG docker $USER

On Ubuntu 22.04, swap dnf for apt and use https://download.docker.com/linux/ubuntu. Verify with docker compose version — note the space, not the hyphen. The legacy docker-compose (v1, Python) is end-of-life and missing features the v2 plugin has had for years.

Step 3: Deploy n8n with Docker Compose and PostgreSQL

For anything beyond a single-user demo, use PostgreSQL — n8n's default SQLite breaks under concurrent webhook load and is not safe for backup-while-running. Create /opt/n8n/docker-compose.yml:

services:
  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: n8n
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n"]
      interval: 5s
      timeout: 5s
      retries: 10

  n8n:
    image: docker.n8n.io/n8nio/n8n:latest
    restart: unless-stopped
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      DB_TYPE: postgresdb
      DB_POSTGRESDB_HOST: postgres
      DB_POSTGRESDB_PORT: 5432
      DB_POSTGRESDB_DATABASE: n8n
      DB_POSTGRESDB_USER: n8n
      DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
      N8N_HOST: ${N8N_HOST}
      N8N_PROTOCOL: https
      WEBHOOK_URL: https://${N8N_HOST}/
      N8N_PORT: 5678
      GENERIC_TIMEZONE: ${TZ:-UTC}
      TZ: ${TZ:-UTC}
      N8N_BASIC_AUTH_ACTIVE: "true"
      N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER}
      N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD}
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  postgres_data:
  n8n_data:

Key things this fixes that most quickstarts get wrong:

  • 127.0.0.1:5678:5678 binds n8n to localhost only. Without that prefix, Docker punches through ufw/firewalld and exposes port 5678 to the public internet directly, bypassing your reverse proxy.
  • WEBHOOK_URL is mandatory if you intend to receive webhooks from third parties. n8n builds webhook URLs from this — get it wrong and Stripe, GitHub, and friends post to URLs that 404.
  • No version: key — Compose v2 ignores it and the spec dropped the field.
  • Healthcheck on Postgres + depends_on: condition: service_healthy prevents n8n from racing the database on cold boot, which manifests as cryptic ECONNREFUSED errors in the logs.

Create /opt/n8n/.env next to it (and chmod 600 .env):

POSTGRES_PASSWORD=$(openssl rand -base64 32)
N8N_HOST=n8n.yourdomain.com
TZ=Europe/Madrid
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=$(openssl rand -base64 24)

Then bring it up:

cd /opt/n8n
docker compose up -d
docker compose logs -f n8n

You should see Editor is now accessible via: http://localhost:5678/ within a few seconds.

Step 4: Front n8n with Nginx and Let's Encrypt

n8n speaks plain HTTP. Putting it behind Nginx gives you TLS, HTTP/2, and a place to add IP allowlists or fail2ban later.

# Alibaba Cloud Linux 3
sudo dnf install -y nginx certbot python3-certbot-nginx

# Ubuntu
sudo apt install -y nginx certbot python3-certbot-nginx

Create /etc/nginx/conf.d/n8n.conf:

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

    # Required for n8n's editor websocket
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    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;

    # n8n streams long-running executions
    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;

    # Webhook payloads can be larger than nginx's 1MB default
    client_max_body_size 16M;

    location / {
        proxy_pass http://127.0.0.1:5678;
    }
}

Then issue a certificate:

sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d n8n.yourdomain.com

Certbot installs a systemd timer (or cron job, depending on the distro) that runs twice a day and renews any certificate inside its 30-day window. You do not need to renew manually every 90 days — verify the timer with systemctl list-timers | grep certbot and you can forget about it.

Step 5: Automated updates with Watchtower (sanely configured)

Most Watchtower tutorials run with --interval 30, which checks for new images every 30 seconds — a great way to burn registry rate limits and roll out half-tested releases the moment they hit Docker Hub. For production n8n, daily checks at a quiet hour are plenty:

Add this service to your docker-compose.yml:

  watchtower:
    image: containrrr/watchtower
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      WATCHTOWER_CLEANUP: "true"
      WATCHTOWER_INCLUDE_RESTARTING: "true"
      WATCHTOWER_NOTIFICATION_REPORT: "true"
    command: --schedule "0 0 4 * * *" n8n

What changed:

  • Cron-style schedule at 04:00 daily instead of every 30 seconds.
  • Container allowlist (n8n at the end of the command) so Watchtower never auto-updates Postgres — major Postgres upgrades require a pg_dump migration, not a cold container swap.
  • WATCHTOWER_CLEANUP removes old images so a 50 GB disk does not fill up with stale layers.

If you want the deployment trigger to come from your repo rather than the public n8n image — say, you fork n8n or maintain your own custom-node bundle — Watchtower is the wrong tool. DeployHQ is a better fit: build the image in your pipeline, push it to your registry, and let an automatic deployment hook run docker compose pull && docker compose up -d on the VPS. That gives you one-click rollback and a deployment history you can audit, neither of which Watchtower offers. The general pattern is the same one we cover in how to automate deployments.

Manual updates (when you need them)

For a one-off update — say, before a major n8n release with breaking changes you want to test — bypass Watchtower:

cd /opt/n8n

# Snapshot the database first
docker compose exec postgres pg_dump -U n8n n8n | gzip > backup-$(date +%F).sql.gz

# Pull and restart
docker compose pull n8n
docker compose up -d n8n

# Tail the logs to confirm
docker compose logs -f n8n

To stop n8n cleanly without removing the volumes, run docker compose stop n8ndocker compose down removes containers, networks, and (with -v) the volume holding your encrypted credentials.

Production checklist

A self-hosted n8n install that lasts more than a quarter usually has all of these:

  1. Strong basic auth credentials stored in a password manager, not the same admin/admin you used during setup.
  2. N8N_ENCRYPTION_KEY set explicitly. n8n auto-generates one on first boot, but if you ever restore the volume on a new host without that key, every saved credential decrypts to garbage.
  3. Nightly Postgres backups off-server. The pg_dump line above plus rclone to object storage takes ten minutes to set up and saves you the day Alibaba Cloud's underlying disk has a bad afternoon.
  4. Outbound monitoring — UptimeRobot or healthchecks.io pinging https://n8n.yourdomain.com/healthz. n8n's own monitoring lives inside n8n, which is no help when n8n is down.
  5. Firewall locked down — SSH from your IP, HTTPS open, everything else closed. docker ignores ufw rules unless you bind containers to 127.0.0.1 (which Step 3 does).
  6. A staging instance — a second cheap VPS (or Docker Compose stack on your laptop) where you test n8n upgrades before they hit production. The same Git-based deployment workflow covers both.

Wrapping up

You now have n8n running on an Alibaba Cloud VPS with PostgreSQL, TLS, basic auth, and either daily-image updates via Watchtower or audited deploys via DeployHQ. The same Docker Compose pattern works for any of the other tools we have covered — see self-hosting Metabase, Keycloak on a VPS, or our broader take on the easiest way to deploy on a VPS.

If you want the deployment loop without the Watchtower trade-offs, start a free DeployHQ trial and connect your repo — your next n8n upgrade will be a git push. Pricing details are on the plans page.


Questions or stuck on a specific Alibaba Cloud quirk? Email us at support@deployhq.com or ping @deployhq on X.