Setting up a local n8n dev environment on Windows using WSL, Docker Desktop, AlmaLinux and Cloudflare Tunnel

Note: this article is still a Work In Progress (WIP) detailing the steps I took setting up my own n8n local development environment in my Windows machine. The goal for now is for the article to serve as a reference for personal use. I do plan to update and make it readable and helpful as well to readers and other folks who might be trying to do the same thing.

1. Dev environment on Windows using WSL, Docker Desktop, AlmaLinux, n8n, Cloudflare Tunnel

1.1 Install WSL and AlmaLinux

  1. In PowerShell as Administrator:
wsl --install --no-distribution

Reboot if prompted.

  1. After reboot, list available distros:
wsl --list --online

Install AlmaLinux 9:

wsl --install AlmaLinux-9

Complete the first‑run setup (Linux username/password).

  1. Ensure it runs as WSL2:
wsl --list --verbose

If AlmaLinux isn’t VERSION 2:

wsl --set-version AlmaLinux-9 2

1.2 Configure Docker Desktop with WSL

  1. Install Docker Desktop for Windows.

  2. In Docker Desktop → Settings → Resources → WSL integration:

    Enable integration for AlmaLinux-9.

  3. Open an AlmaLinux shell from Windows Terminal or Start menu.

1.3 Project layout & env files

In project folder (for example, C:\Users\romme\OneDrive\Documents\Projects\n8n), layout should be something like below:

n8n/
  docker-compose.yml          # base
  docker-compose.dev.yml      # dev overlay
  .env.dev                    # dev env vars
  .cloudflared.env            # Cloudflare tunnel token
  Makefile
  data/                       # created by n8n

.env.dev (example):

N8N_BASIC_AUTH_ACTIVE=true
N8N_BASIC_AUTH_USER=...
N8N_BASIC_AUTH_PASSWORD=...

N8N_HOST=dev-n8n.example.com
WEBHOOK_URL=https://dev-n8n.example.com/

DB_POSTGRESDB_HOST=postgres
DB_POSTGRESDB_PORT=5432
DB_POSTGRESDB_DATABASE=n8n
DB_POSTGRESDB_USER=dev-n8n-pgdb-user
DB_POSTGRESDB_PASSWORD=...

POSTGRES_DB=n8n
POSTGRES_USER=dev-n8n-pgdb-user
POSTGRES_PASSWORD=...

1.4 Base docker-compose (n8n + Postgres)

docker-compose.yml:

services:
  n8n:
    image: n8nio/n8n:2.1.1
    restart: always
    ports:
      - "5678:5678"
    environment:
      - N8N_BASIC_AUTH_ACTIVE=${N8N_BASIC_AUTH_ACTIVE}
      - N8N_BASIC_AUTH_USER=${N8N_BASIC_AUTH_USER}
      - N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD}
      - N8N_HOST=${N8N_HOST}
      - N8N_PORT=5678
      - WEBHOOK_URL=${WEBHOOK_URL}
      - EXECUTIONS_PROCESS=main
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=${DB_POSTGRESDB_HOST}
      - DB_POSTGRESDB_PORT=${DB_POSTGRESDB_PORT}
      - DB_POSTGRESDB_DATABASE=${DB_POSTGRESDB_DATABASE}
      - DB_POSTGRESDB_USER=${DB_POSTGRESDB_USER}
      - DB_POSTGRESDB_PASSWORD=${DB_POSTGRESDB_PASSWORD}
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      - postgres
    networks:
      - n8n_network

  postgres:
    image: postgres:13
    restart: always
    environment:
      - POSTGRES_DB=${POSTGRES_DB}
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
    volumes:
      - n8n_postgres_data:/var/lib/postgresql/data
    networks:
      - n8n_network

volumes:
  n8n_data:
  n8n_postgres_data:

networks:
  n8n_network:
    driver: bridge

This file is shared by dev/QA/prod; differences come through env files and overlays. ​

1.5 Dev overlay with Cloudflare Tunnel

docker-compose.dev.yml:

services:
  cloudflared:
    image: cloudflare/cloudflared:latest
    restart: unless-stopped
    command: tunnel run
    env_file:
      - .cloudflared.env
    depends_on:
      - n8n
    networks:
      - n8n_network

.cloudflared.env:

TUNNEL_TOKEN=your-cloudflare-tunnel-token

On the Cloudflare side:

  1. Create a tunnel in Cloudflare Zero Trust → Access → Tunnels. ​

  2. Configure a Public hostname:

    Hostname: dev-n8n.example.com

    Type: HTTP

    Service URL: n8n:5678 (no http://). ​

Cloudflare’s DNS gets a CNAME for dev-n8n.example.com pointing to the tunnel.

1.6 Running the dev stack from AlmaLinux WSL

From AlmaLinux in the project directory:

docker compose \
  -f docker-compose.yml \
  -f docker-compose.dev.yml \
  --env-file .env.dev up -d

Then access:

https://n8n-dev.0xrommel.dev

Cloudflare Tunnel forwards that to the local n8n container.

2. Best practices for docker-compose files

  1. Base + overlays pattern

    • docker-compose.yml for shared configuration.

    • docker-compose.dev.yml, docker-compose.prod.yml for environment‑specific overrides (labels, extra services, debug tools, tunnels, ingress).

  2. Use env files, never hard‑code secrets

    • Reference variables in environment: using ${VAR}.

    • Commit .example env files; ignore real .env. in Git.

  3. Separate concerns via networks

    • Internal app DB network (n8n_network).

    • Optional external/proxy network (traefik_default) marked as external: true only when you actually have a Traefik stack creating it. ​

  4. Use official images where possible

    • n8nio/n8n and postgres:13 straight from Docker Hub for simplicity and updates. ​
  5. Port mappings and health

    • Explicit ports in dev (“5678:5678”) so you can reach services directly.

    • In prod, you can drop host ports and only expose via proxy (Traefik/Ingress).

  6. Don’t over‑customize the app image unless necessary

    • For this dev setup, you dropped the custom Dockerfile and used the official n8n image, avoiding base‑OS/package‑manager issues and keeping parity with upstream.

3. Makefile to simplify Docker commands

Instead of typing the full compose command, you added a Makefile.

Makefile:

DEV_COMPOSE_FILES = -f docker-compose.yml -f docker-compose.dev.yml
DEV_ENV_FILE = --env-file .env.dev

dev-up:
	docker compose $(DEV_COMPOSE_FILES) $(DEV_ENV_FILE) up -d

dev-down:
	docker compose $(DEV_COMPOSE_FILES) $(DEV_ENV_FILE) down

dev-logs:
	docker compose $(DEV_COMPOSE_FILES) $(DEV_ENV_FILE) logs -f

dev-ps:
	docker compose $(DEV_COMPOSE_FILES) $(DEV_ENV_FILE) ps

Use it from AlmaLinux WSL:

make dev-up     # start n8n + postgres + cloudflared
make dev-logs   # follow logs
make dev-ps     # see status
make dev-down   # stop everything

On AlmaLinux, install make once if needed:

sudo dnf install -y make

Voila! In the end, you get:

  • A Windows dev environment that runs Linux containers via WSL2.
  • n8n + Postgres matching how you’ll deploy on AlmaLinux.
  • Cloudflare Tunnel providing a real HTTPS dev URL.
  • Clean, versioned Compose files with environment overlays.
  • A Makefile you can use to spin the stack up and down with a single, memorable command.