Hey n8n fam ,
I wanted to share my Docker-based full dev container setup I’ve been building to make working with n8n smoother, especially when working with queues, databases, and performance tuning.
Key Features
n8n + n8n-worker (with queue mode support) Disabled in Dockerfile, enable by removing comments
PostgreSQL + pgAdmin
Redis + RedisInsight
Bull Board for queue debugging
Prometheus + Grafana for metrics
Clean volume separation for data persistence
.env-driven configuration for secrets
Health checks and restart policies for dev reliability
Services
Service | Description | Port |
---|---|---|
n8n | Main n8n app | 5678 |
n8n-worker | Executes jobs in queue mode | — |
PostgreSQL | Database backend | 5432 |
pgAdmin | PostgreSQL GUI | 5050 |
Redis | Queue engine | 6379 |
RedisInsight | GUI for Redis debugging | 8001 |
Bull Board | Monitor Bull queues (n8n jobs) | 3002 |
Prometheus | Metrics collector | 9090 |
Grafana | Dashboards for observability | 3003 |
File in folder
For Grafana and Prometheus see here details
docker-compose.yml
version: '3.8'
volumes:
db_storage:
n8n_storage:
redis_storage:
pgadmin_data:
prometheus_data:
grafana_data:
x-shared: &shared
restart: always
image: docker.n8n.io/n8nio/n8n
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
- DB_POSTGRESDB_USER=${POSTGRES_NON_ROOT_USER}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_NON_ROOT_PASSWORD}
##- EXECUTIONS_MODE=queue
##- QUEUE_MODE=redis
##- QUEUE_BULL_REDIS_HOST=redis
##- QUEUE_BULL_REDIS_PORT=6379
##- QUEUE_HEALTH_CHECK_ACTIVE=true
- N8N_DEFAULT_BINARY_DATA_MODE=filesystem
- N8N_ENCRYPTION_KEY=${ENCRYPTION_KEY}
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
- N8N_HOST=localhost
- N8N_PORT=5678
- N8N_PROTOCOL=http
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=${N8N_USER}
- N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD}
- N8N_LOG_OUTPUT=file
- n8n.log.level=debug
- N8N_METRICS=true
- N8N_RUNNERS_ENABLED=true
links:
- postgres
- redis
volumes:
- n8n_storage:/home/node/.n8n
depends_on:
redis:
condition: service_healthy
postgres:
condition: service_healthy
services:
postgres:
image: postgres:16
restart: always
environment:
- POSTGRES_USER
- POSTGRES_PASSWORD
- POSTGRES_DB
- POSTGRES_NON_ROOT_USER
- POSTGRES_NON_ROOT_PASSWORD
volumes:
- db_storage:/var/lib/postgresql/data
- ./init-data.sh:/docker-entrypoint-initdb.d/init-data.sh
healthcheck:
test: ['CMD-SHELL', 'pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}']
interval: 5s
timeout: 5s
retries: 10
redis:
image: redis:6-alpine
restart: always
volumes:
- redis_storage:/data
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 5s
timeout: 5s
retries: 10
n8n:
<<: *shared
ports:
- 5678:5678
user: root
n8n-worker:
<<: *shared
command: worker
depends_on:
- n8n
redisinsight:
image: redislabs/redisinsight:1.14.0
container_name: redisinsight
ports:
- "8001:8001"
restart: always
depends_on:
redis:
condition: service_healthy
bull-board:
build:
context: .
dockerfile: Dockerfile.bullboard
container_name: bull-board
ports:
- "3002:3002"
environment:
- REDIS_HOST=redis
depends_on:
redis:
condition: service_healthy
pgadmin:
image: dpage/pgadmin4
container_name: pgadmin
restart: always
ports:
- "5050:80"
volumes:
- pgadmin_data:/var/lib/pgadmin
environment:
PGADMIN_DEFAULT_EMAIL: [email protected]
PGADMIN_DEFAULT_PASSWORD: admin
depends_on:
postgres:
condition: service_healthy
prometheus:
image: prom/prometheus
container_name: prometheus
restart: always
ports:
- "9090:9090"
volumes:
- prometheus_data:/prometheus
- ./prometheus.yml:/etc/prometheus/prometheus.yml
command:
- '--config.file=/etc/prometheus/prometheus.yml'
depends_on:
- n8n
grafana:
image: grafana/grafana
container_name: grafana
restart: always
ports:
- "3003:3000"
volumes:
- grafana_data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin
depends_on:
- prometheus
Dockerfile.bullboard
FROM node:18-alpine
WORKDIR /app
# Install all necessary packages
RUN npm install express @bull-board/api @bull-board/ui @bull-board/express bull ioredis
# Add the minimal express server
COPY server.js .
EXPOSE 3002
CMD ["node", "server.js"]
server.js
const express = require('express');
const { createBullBoard } = require('@bull-board/api');
const { BullAdapter } = require('@bull-board/api/bullAdapter');
const { ExpressAdapter } = require('@bull-board/express');
const Queue = require('bull');
// Setup the Bull Board UI using Express
const serverAdapter = new ExpressAdapter();
serverAdapter.setBasePath('/ui');
// This must match the queue name and prefix used by n8n
const jobsQueue = new Queue('jobs', {
redis: {
host: process.env.REDIS_HOST || 'redis',
port: 6379,
},
prefix: 'bull' // <-- very important: this is what n8n uses
});
// Register queues with Bull Board
const { addQueue, removeQueue, setQueues, replaceQueues } = createBullBoard({
queues: [new BullAdapter(jobsQueue)],
serverAdapter,
});
// Start the Express server
const app = express();
app.use('/ui', serverAdapter.getRouter());
app.listen(3002, () => {
console.log('🚀 Bull Board is running at http://localhost:3002/ui');
});
Contributions & Ideas Welcome
Open to suggestions, tweaks, or contributions. If you’re running more services, feel free to extend this setup and share yours!
Cheers,
@King_Samuel_David
btw, I spin up other versions for testing like this:
docker run -d
–name n8n
–user root
-p 0:5678
-v “$(pwd)”:/home/node/.n8n
-e N8N_ENCRYPTION_KEY=‘KEY’
-e N8N_BASIC_AUTH_ACTIVE=false
n8nio/n8n:1.71.0