Technical • 4 min read
How I Built My Own Automation Server (Without Breaking Linux)
Nazmul • Published 2/28/2026 • Updated 2/28/2026
How I Installed n8n with Docker + PostgreSQL on Linux and Accessed It Remotely
If you want a reliable self-hosted n8n setup on a Linux laptop or server without buying a domain yet, this is a practical and clean path that works.
This setup is simple, reproducible, and stable enough to grow.
---
Why this setup
I chose:
• Docker + Docker Compose for easy deployment, isolation, and updates
• PostgreSQL as the n8n database (more robust than SQLite for scaling)
• Port 5678 for the n8n web UI
• LAN / SSH access for remote usage (no Cloudflare or domain required yet)
This keeps everything controlled locally while still allowing secure remote access when needed.
---
- Verify Docker is installed
First, confirm Docker and Docker Compose are working:
bash1 docker --version 2 docker compose version
If Docker gives a daemon error, reset and start the service:
bash1 sudo systemctl reset-failed docker.service docker.socket 2 sudo systemctl start docker.socket 3 sudo systemctl start docker.service
Test Docker:
bash1 docker run --rm hello-world
If that runs successfully, Docker is ready.
---
- Create project folder
I prefer storing stacks under /opt/stacks.
bash1 sudo mkdir -p /opt/stacks/n8n 2 sudo chown -R nazmul:nazmul /opt/stacks/n8n 3 cd /opt/stacks/n8n
Make sure you replace nazmul with your actual Linux username if different.
---
- Check free port
Before binding ports, verify that 5678 is not already in use.
If ports like 22, 3000, or 3389 are already active, that’s fine. Just ensure 5678 is free.
You can check:
bash1 sudo ss -tulnp | grep 5678
If nothing shows, the port is available.
---
- Create .env for n8n + PostgreSQL
Create the environment file:
bash1 cat > .env <<'EOF' 2 N8N_PORT=5678 3 N8N_HOST=localhost 4 N8N_PROTOCOL=http 5 N8N_EDITOR_BASE_URL=http://localhost:5678 6 WEBHOOK_URL=http://localhost:5678 7 8 N8N_BASIC_AUTH_ACTIVE=true 9 N8N_BASIC_AUTH_USER=admin 10 N8N_BASIC_AUTH_PASSWORD=ChangeThis_n8nAuth_123 11 12 GENERIC_TIMEZONE=America/New_York 13 TZ=America/New_York 14 15 POSTGRES_DB=n8n 16 POSTGRES_USER=n8n 17 POSTGRES_PASSWORD=ChangeThis_pgPass_123 18 EOF
Important: change both passwords before production use.
---
- Create docker-compose.yml
Create the compose file:
yaml1 services: 2 postgres: 3 image: postgres:16 4 container_name: n8n-postgres 5 restart: unless-stopped 6 environment: 7 - POSTGRES_DB=${POSTGRES_DB} 8 - POSTGRES_USER=${POSTGRES_USER} 9 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} 10 volumes: 11 - postgres_data:/var/lib/postgresql/data 12 healthcheck: 13 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] 14 interval: 10s 15 timeout: 5s 16 retries: 10 17 18 n8n: 19 image: n8nio/n8n:latest 20 container_name: n8n 21 restart: unless-stopped 22 ports: 23 - "${N8N_PORT}:5678" 24 depends_on: 25 postgres: 26 condition: service_healthy 27 env_file: 28 - .env 29 environment: 30 - N8N_HOST=${N8N_HOST} 31 - N8N_PORT=5678 32 - N8N_PROTOCOL=${N8N_PROTOCOL} 33 - N8N_EDITOR_BASE_URL=${N8N_EDITOR_BASE_URL} 34 - WEBHOOK_URL=${WEBHOOK_URL} 35 - N8N_BASIC_AUTH_ACTIVE=${N8N_BASIC_AUTH_ACTIVE} 36 - N8N_BASIC_AUTH_USER=${N8N_BASIC_AUTH_USER} 37 - N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD} 38 - GENERIC_TIMEZONE=${GENERIC_TIMEZONE} 39 - TZ=${TZ} 40 - DB_TYPE=postgresdb 41 - DB_POSTGRESDB_HOST=postgres 42 - DB_POSTGRESDB_PORT=5432 43 - DB_POSTGRESDB_DATABASE=${POSTGRES_DB} 44 - DB_POSTGRESDB_USER=${POSTGRES_USER} 45 - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD} 46 volumes: 47 - n8n_data:/home/node/.n8n 48 49 volumes: 50 n8n_data: 51 postgres_data:
Make sure indentation is correct. YAML is sensitive to spacing.
---
- Start the stack
Start everything in detached mode:
bash1 docker compose up -d 2 docker compose ps
Expected result:
• n8n container running on 0.0.0.0:5678
• n8n-postgres marked healthy
If something fails:
bash1 docker compose logs -f n8n
---
- Access n8n
Local machine:
Same network (LAN):
Replace the IP with your machine’s LAN address.
This is the easiest way to access n8n from another device on the same Wi-Fi network.
---
- If you are outside your home network (no domain yet)
Use SSH tunneling from a remote machine.
From your remote PC:
bash1 ssh -L 5678:localhost:5678 nazmul@[public_ip] 2
To know public ip u can use "curl ifconfig.me"
Then open in your browser:
This securely tunnels traffic through SSH without exposing public ports.
---
- Common issues and fixes
“Cannot connect to Docker daemon”
Docker service was down. Start/reset the service as shown earlier.
YAML mapping key errors in docker-compose
Usually caused by broken indentation or duplicate keys. Rewrite the file carefully.
chown: invalid user serveradmin:serveradmin
That user does not exist. Use an existing Linux user:
bash1 sudo chown -R nazmul:nazmul /opt/stacks
---
- Useful lifecycle commands
Navigate to the stack:
bash1 cd /opt/stacks/n8n
Stop everything:
bash1 docker compose down
Start again:
bash1 docker compose up -d
Restart only n8n:
bash1 docker compose restart n8n
Update images:
bash1 docker compose pull 2 docker compose up -d
---
This setup gives you:
• Persistent PostgreSQL database
• Persistent n8n data
• Clean separation via Docker
• Secure remote access via SSH
• No domain required
When ready later, you can extend this with:
• Cloudflare Tunnel
• Custom domain + HTTPS
• Reverse proxy (Nginx or Traefik)
• VPS deployment
This is a solid self-hosted foundation that you can grow into production.
More in Technical