The real appeal of n8n is running it on your own server. Data stays inside, there's no per-execution billing, and you can be at the first screen in 30 minutes. This post takes you from a single Docker line to something close to a production setup in one pass.
Why self-host
- Data sovereignty — customer data never leaves your infrastructure
- Flat pricing — one server bill, no matter how many executions
- Customization — Community nodes, Code nodes, internal APIs, all fair game
- Speed — calling ERP from inside your network means near-zero latency
What you need
- A Linux server with Docker installed (2 cores / 4GB RAM recommended)
- A domain to point at it (optional, required if you want HTTPS)
- A terminal and 5 minutes of patience
1. Bring it up in one line
The fastest way is to spin up a single Docker container. Get the volume right and workflows plus credentials survive restarts.
docker run -d \
--name n8n \
--restart unless-stopped \
-p 5678:5678 \
-v n8n_data:/home/node/.n8n \
-e GENERIC_TIMEZONE=Asia/Seoul \
-e TZ=Asia/Seoul \
docker.n8n.io/n8nio/n8nOpen http://localhost:5678 (or your server's IP) in a browser and you'll see the owner account creation screen. Enter email and password and you're on the canvas. That's 5 minutes.
2. Verify data persistence
The key line in the command above is -v n8n_data:/home/node/.n8n. Workflow JSON, credentials, and execution logs all live in that volume. Delete the container and your data survives as long as the volume does.
docker volume inspect n8n_data
docker exec n8n ls /home/node/.n8n3. The architecture
4. For real workloads, use docker-compose
If this is real work and not a toy, skip the standalone container and pair n8n with PostgreSQL via docker-compose. Separating the database transforms backup, upgrade, and performance tuning into an entirely different game.
services:
postgres:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_USER: n8n
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: n8n
volumes:
- pg_data:/var/lib/postgresql/data
n8n:
image: docker.n8n.io/n8nio/n8n
restart: unless-stopped
ports:
- '5678:5678'
environment:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_USER: n8n
DB_POSTGRESDB_PASSWORD: ${DB_PASSWORD}
N8N_HOST: n8n.example.com
N8N_PROTOCOL: https
WEBHOOK_URL: https://n8n.example.com/
volumes:
- n8n_data:/home/node/.n8n
depends_on:
- postgres
volumes:
pg_data:
n8n_data:- Save the file above as
docker-compose.yml - Create a
.envfile in the same folder and fill inDB_PASSWORD=... - Run
docker compose up -d - Watch startup with
docker compose logs -f n8n - Open the domain in a browser and create the first account
5. HTTPS through a reverse proxy
The n8n container itself serves plain HTTP on port 5678. In production, put Nginx or Caddy in front and let Let's Encrypt handle renewals automatically. Caddy takes three lines of config.
n8n.example.com {
reverse_proxy localhost:5678
}WEBHOOK_URL env var to your actual domain so externally-issued webhook URLs come out as HTTPS. Skip this and your webhook URLs end up as http://localhost:5678/..., which external services can't reach.6. Backups — do it now, before you forget
- PostgreSQL dump to S3 or external storage nightly
- Snapshot of the
n8n_datavolume once a week - A restore drill at least once per quarter
Self-hosting isn't "server administration" — it's data sovereignty. Get it up once and the workflows do the rest.— SynAct.ai infrastructure
Common traps
- Skipped the volume, deleted the container, lost every workflow
- Forgot
WEBHOOK_URL— external webhooks can't connect - Timezone stays at UTC and schedules run 9 hours off
- 2GB of memory, OOM the moment a large execution runs
Frequently asked
- Q. n8n Cloud vs self-hosted — how do I choose? A. Self-hosted wins when data must stay in-country, execution volume is high, or you need custom nodes / credentials. Cloud has lower total cost of ownership for teams of five or fewer without an infra person on hand.
- Q. Docker Compose or Kubernetes? A. Docker Compose is enough for a single server. Reach for K8s only when you need to scale worker nodes horizontally (roughly 1M+ executions/month). In between, adding a separate worker container to Compose is the most common practical choice.
- Q. How do backups work? Is dumping the DB enough? A. If you're on PostgreSQL, workflows, credentials, and execution history all live in the DB —
pg_dumpis the baseline. But you also need the credential encryption key (N8N_ENCRYPTION_KEY) and the~/.n8ndirectory (config files) to do a full restore. - Q. How do I add HTTPS? A. Simplest combo is Caddy or Nginx + Let's Encrypt. Add one caddy container to your Docker Compose, point it at your domain, and certificates get issued and renewed automatically. On a private network, mount a cert issued by your internal CA.
- Q. PostgreSQL vs SQLite — which one should I start with? A. PostgreSQL is the production answer. SQLite is dev/demo only. Once concurrency grows, SQLite locks start failing workflows, and backups/migrations get harder. Start with PostgreSQL and you never need to move.
- Q. What do I watch out for on upgrades? A. Three things — (1) check release notes for DB migrations, (2) pre-test any custom nodes or credentials against the new version, (3)
pg_dump+ back upN8N_ENCRYPTION_KEYright before upgrading. Do those three and rolling upgrades are downtime-free.
What's next
If you made it this far, you already own an automation server that holds its own against anything internal. All that's missing is workflows. The next post covers distributing executions with queue mode, plus monitoring and alerts.