apiVersion: v1
kind: Pod
metadata:
name: n8n
labels:
app: n8n
spec:
restartPolicy: Always
containers:
- name: n8n
image: docker.io/n8nio/n8n:latest
ports:
- hostPort: 5678
containerPort: 5678
env:
- name: DB_TYPE
value: postgresdb
- name: DB_POSTGRESDB_HOST
value: 127.0.0.1
- name: DB_POSTGRESDB_PORT
value: '5432'
- name: DB_POSTGRESDB_DATABASE
value: n8n
- name: DB_POSTGRESDB_USER
value: n8n
- name: DB_POSTGRESDB_PASSWORD
value: KK0yQtPwAL7yGlB0fxt4Zg
- name: N8N_HOST
value: localhost
- name: N8N_PORT
value: '5678'
- name: N8N_PROTOCOL
value: http
- name: WEBHOOK_URL
value: http://localhost:5678/
- name: GENERIC_TIMEZONE
value: Europe/Vienna
volumeMounts:
- name: vol-0
mountPath: /home/node/.n8n
securityContext:
runAsUser: 1000
runAsGroup: 1000
- name: postgres
image: docker.io/postgres:16
ports:
- hostPort: 5432
containerPort: 5432
env:
- name: POSTGRES_USER
value: n8n
- name: POSTGRES_PASSWORD
value: KK0yQtPwAL7yGlB0fxt4Zg
- name: POSTGRES_DB
value: n8n
volumeMounts:
- name: vol-1
mountPath: /var/lib/postgresql/data
volumes:
- name: vol-0
hostPath:
path: ~/n8n/data
type: DirectoryOrCreate
- name: vol-1
hostPath:
path: ~/n8n/db
type: DirectoryOrCreate
#!/bin/bash set -e # ── Pod erstellen ────────────────────────────────────────────────── podman pod create \ --name n8n \ -p 5678:5678 \ -p 5432:5432 # ── Container: n8n ───────────────────────────────────────────── podman run -d \ --pod n8n \ --name n8n \ --restart always \ -e DB_TYPE="postgresdb" \ -e DB_POSTGRESDB_HOST="127.0.0.1" \ -e DB_POSTGRESDB_PORT="5432" \ -e DB_POSTGRESDB_DATABASE="n8n" \ -e DB_POSTGRESDB_USER="n8n" \ -e DB_POSTGRESDB_PASSWORD="KK0yQtPwAL7yGlB0fxt4Zg" \ -e N8N_HOST="localhost" \ -e N8N_PORT="5678" \ -e N8N_PROTOCOL="http" \ -e WEBHOOK_URL="http://localhost:5678/" \ -e GENERIC_TIMEZONE="Europe/Vienna" \ -v ~/n8n/data:/home/node/.n8n:Z \ --user 1000:1000 \ docker.io/n8nio/n8n:latest # ── Container: postgres ───────────────────────────────────────────── podman run -d \ --pod n8n \ --name postgres \ --restart always \ -e POSTGRES_USER="n8n" \ -e POSTGRES_PASSWORD="KK0yQtPwAL7yGlB0fxt4Zg" \ -e POSTGRES_DB="n8n" \ -v ~/n8n/db:/var/lib/postgresql/data:Z \ --user 999:999 \ docker.io/postgres:16 # ── Stoppen & Aufräumen ──────────────────────────────────────────── # podman pod stop n8n # podman pod rm n8n
Prerequisites once as root
0. Install Podman
apt update && apt install -y podman
1. Create user (if not existing)
useradd -m -s /bin/bash n8n passwd n8n
2. Enable linger (service runs after reboot without login)
loginctl enable-linger n8n
Save the YAML file
mkdir -p ~/.config/containers/ # Copy the YAML above to: nano ~/.config/containers/n8n.yaml
Test the pod (without autostart)
podman play kube ~/.config/containers/n8n.yaml # Check status: podman pod ps && podman ps # Stop: podman play kube --down ~/.config/containers/n8n.yaml
Create Quadlet .kube file
Place it at ~/.config/containers/systemd/n8n.kube
mkdir -p ~/.config/containers/systemd/ cat > ~/.config/containers/systemd/n8n.kube << 'EOF' [Unit] Description=n8n Pod [Kube] Yaml=%h/.config/containers/n8n.yaml [Install] WantedBy=default.target EOF
Enable systemd service
systemctl --user daemon-reload systemctl --user enable --now n8n-pod.service
Status & Logs
systemctl --user status n8n-pod.service journalctl --user -u n8n-pod.service -f podman pod ps podman ps
Apply image updates
Pull new image versions and restart the pod:
podman pull docker.io/<image>:<tag> podman play kube --replace ~/.config/containers/n8n.yaml # or via systemd: systemctl --user restart n8n-pod.service
Ports < 1024 (e.g. 80, 443)
Rootless cannot open privileged ports. Solution:
sysctl net.ipv4.ip_unprivileged_port_start=80
Make persistent in /etc/sysctl.d/99-podman.conf.
Containers communicate via localhost
All containers in the pod share the same network namespace. Always use localhost, not container names.
# Correct (e.g. app → db): localhost:5432 # Wrong (doesn't work in a pod): db-container:5432
List open ports
Which ports is the running pod listening on?
podman port n8n-pod
Custom DNS for the pod
Set a custom DNS server (e.g. local Pi-hole):
# In YAML under spec.dnsConfig:
spec:
dnsConfig:
nameservers:
- 192.168.1.x
Set volume ownership
Fix permission errors by adjusting UID/GID in the user namespace:
podman unshare chown 1000:1000 /path/to/volume
SELinux volume labels
On SELinux systems (RHEL, Fedora) set the volume suffix:
/host/path:/container/path:Z # private /host/path:/container/path:z # shared
List all volumes
podman volume ls podman volume inspect <volume-name>
Volume backup
Back up data from a named volume:
podman run --rm \ -v <volume-name>:/data:ro \ -v $(pwd):/backup \ busybox tar czf /backup/backup.tar.gz /data
Cleanup
Remove unused images, containers and volumes:
podman system prune -f # containers + images podman image prune -f # untagged images only podman volume prune -f # unused volumes
Automatic image updates (podman-auto-update)
Podman can automatically update images and restart the pod. Enable once:
systemctl --user start podman.socket systemctl --user daemon-reload systemctl --user enable --now podman-auto-update.timer systemctl --user status podman-auto-update.timer
Test without actually updating:
podman auto-update --dry-run
Manual update
Pull a new image version and restart the pod:
podman pull <image>:<tag> podman play kube --replace \ ~/.config/containers/n8n.yaml
Find outdated images
Check local images against the registry:
podman images --filter dangling=false podman pull --all-tags <image>
Shell into a running container
podman exec -it n8n-<container> /bin/sh # or bash: podman exec -it n8n-<container> /bin/bash
Follow live logs
# All containers in the pod: podman pod logs -f n8n-pod # Single container: podman logs -f n8n-<container>
Pod info & resource usage
podman pod inspect n8n-pod podman stats n8n-pod
Restart pod without data loss
podman pod restart n8n-pod # or via systemd: systemctl --user restart n8n-pod.service