Skip to content
How I Built My Own Automation Server (Without Breaking Linux)

Technical4 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.

---

  1. Verify Docker is installed

First, confirm Docker and Docker Compose are working:

bash
1 docker --version 2 docker compose version

If Docker gives a daemon error, reset and start the service:

bash
1 sudo systemctl reset-failed docker.service docker.socket 2 sudo systemctl start docker.socket 3 sudo systemctl start docker.service

Test Docker:

bash
1 docker run --rm hello-world

If that runs successfully, Docker is ready.

---

  1. Create project folder

I prefer storing stacks under /opt/stacks.

bash
1 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.

---

  1. 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:

bash
1 sudo ss -tulnp | grep 5678

If nothing shows, the port is available.

---

  1. Create .env for n8n + PostgreSQL

Create the environment file:

bash
1 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.

---

  1. Create docker-compose.yml

Create the compose file:

yaml
1 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.

---

  1. Start the stack

Start everything in detached mode:

bash
1 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:

bash
1 docker compose logs -f n8n

---

  1. Access n8n

Local machine:

http://localhost:5678

Same network (LAN):

http://192.168.1.104:5678

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.

---

  1. If you are outside your home network (no domain yet)

Use SSH tunneling from a remote machine.

From your remote PC:

bash
1 ssh -L 5678:localhost:5678 nazmul@[public_ip] 2

To know public ip u can use "curl ifconfig.me"

Then open in your browser:

http://localhost:5678

This securely tunnels traffic through SSH without exposing public ports.

---

  1. 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:

bash
1 sudo chown -R nazmul:nazmul /opt/stacks

---

  1. Useful lifecycle commands

Navigate to the stack:

bash
1 cd /opt/stacks/n8n

Stop everything:

bash
1 docker compose down

Start again:

bash
1 docker compose up -d

Restart only n8n:

bash
1 docker compose restart n8n

Update images:

bash
1 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.