This guide shows the exact steps I used to self‑host n8n on a Google Cloud VM with a custom domain on Hostinger DNS. It follows the same Docker Compose + Traefik setup as the original tutorial and includes the extra fixes I needed when things didn’t work the first time.
Stack at a glance: Ubuntu VM (e2‑micro) → Docker + Docker Compose plugin → Traefik reverse proxy (auto‑HTTPS via Let’s Encrypt) → n8n container with persistent volumes. No Nginx.
Prerequisites
- Google Cloud account (Free Tier eligible).
- A domain you control (examples use
example.com; I used Hostinger for DNS). - Basic terminal usage (SSH to VM, edit files).
1) Create Your Google Cloud VM (free tier‑friendly)
- Console: Go to Google Cloud Console → Compute Engine → VM instances → Create instance.
- Name:
n8n-instance(or similar). - Region/Zone: Choose a free‑tier eligible region if you want to stay free (e.g.,
us‑west1,us‑central1,us‑east1). Any region works; free‑tier only matters if you care about zero cost. - Machine type:
e2-microis fine for light use. - Boot disk: Ubuntu 22.04 LTS (increase size to 30 GB for comfort).
- Firewall: Tick Allow HTTP and Allow HTTPS.
- (Optional but recommended) Reserve a static external IP (to avoid DNS breaking after restarts):
- VPC network → External IP addresses → reserve a static address and attach it to this VM.
- Click Create.
Note: In some templates you’ll also see notes about setting a network tag/hostname. It isn’t required for this simple setup.
2) SSH into the VM
Use the SSH → Open in browser window button next to the instance. If you prefer local terminal:
# Replace with your own instance and zone
gcloud compute ssh n8n-instance --zone=us-west1-b
3) Update the OS and install Docker + Docker Compose plugin
sudo apt-get update && sudo apt-get upgrade -y
# Pre-reqs & convenience
sudo apt-get install -y ca-certificates curl nano
# Official Docker repo
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo ${UBUNTU_CODENAME:-$VERSION_CODENAME}) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# (Optional) Run docker as non-root going forward
sudo usermod -aG docker $USER
newgrp docker
docker --version
docker compose version
4) Create a project folder (and fix the missing-folder gotcha)
The tutorial uses a dedicated directory. If cd ~/n8n fails with “No such file or directory”, just create it. I use ~/n8n-compose:
cd ~
mkdir -p n8n-compose
cd ~/n8n-compose
5) Point your domain (Hostinger) at the VM IP
- Grab your VM’s External IP from the VM list.
- In Hostinger → DNS for your domain, create an A record:
- Host/Name:
n8n(or your chosen subdomain) - Points to: your VM’s external IP
- TTL: leave default or 300s
- Host/Name:
- Wait a few minutes. Verify from the VM:
dig +short n8n.example.com
You should see the VM IP. If not, give DNS more time or recheck the record.
6) Environment settings (.env)
Create a .env file that Traefik and n8n will read via Compose labels/environment.
nano .env
Paste and edit:
# Where n8n will be served
DOMAIN_NAME=example.com
SUBDOMAIN=n8n # results in https://n8n.example.com
# Timezone used for cron/scheduling inside n8n
GENERIC_TIMEZONE=Europe/London
# Email for Let’s Encrypt certificate registration
SSL_EMAIL=[email protected]
Save (Ctrl+O, Enter), exit (Ctrl+X).
7) (One-time) Create a shared files directory for workflows
This gives n8n access to a host directory at /files inside the container.
mkdir -p ./local-files
ls -la
8) Docker Compose file (Traefik + n8n)
Create the docker-compose.yml:
nano docker-compose.yml
Paste the following:
services:
traefik:
image: traefik
restart: always
command:
- "--api=true"
- "--api.insecure=true" # Consider disabling in production
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"
- "--certificatesresolvers.mytlschallenge.acme.email=${SSL_EMAIL}"
- "--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
volumes:
- traefik_data:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock:ro
n8n:
image: docker.n8n.io/n8nio/n8n
restart: always
ports:
- "127.0.0.1:5678:5678" # internal bind; Traefik handles public 443
labels:
- traefik.enable=true
- traefik.http.routers.n8n.rule=Host(`${SUBDOMAIN}.${DOMAIN_NAME}`)
- traefik.http.routers.n8n.tls=true
- traefik.http.routers.n8n.entrypoints=web,websecure
- traefik.http.routers.n8n.tls.certresolver=mytlschallenge
- traefik.http.middlewares.n8n.headers.SSLRedirect=true
- traefik.http.middlewares.n8n.headers.STSSeconds=315360000
- traefik.http.middlewares.n8n.headers.browserXSSFilter=true
- traefik.http.middlewares.n8n.headers.contentTypeNosniff=true
- traefik.http.middlewares.n8n.headers.forceSTSHeader=true
- traefik.http.middlewares.n8n.headers.SSLHost=${DOMAIN_NAME}
- traefik.http.middlewares.n8n.headers.STSIncludeSubdomains=true
- traefik.http.middlewares.n8n.headers.STSPreload=true
- traefik.http.routers.n8n.middlewares=n8n@docker
environment:
- N8N_HOST=${SUBDOMAIN}.${DOMAIN_NAME}
- N8N_PORT=5678
- N8N_PROTOCOL=https
- NODE_ENV=production
- WEBHOOK_URL=https://${SUBDOMAIN}.${DOMAIN_NAME}/
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
volumes:
- n8n_data:/home/node/.n8n
- ./local-files:/files
volumes:
n8n_data:
traefik_data:
Save and exit.
9) Launch the stack
# From the ~/n8n-compose directory
sudo docker compose up -d
# Verify containers
docker compose ps
# Tail logs if you need to troubleshoot
docker compose logs -f traefik
docker compose logs -f n8n
Give it ~1–3 minutes on first run (Traefik will obtain certificates).
10) Finish n8n setup in the browser
Open https://n8n.example.com (replace with your domain). You should see the Owner account setup screen. Create your account and (optionally) activate the Fair Code features using your email.
Troubleshooting & Fixes I Needed
A) cd ~/n8n or cd ~/n8n-compose says “No such file or directory”
You’re just in the wrong path or the folder wasn’t created yet. Run:
cd ~ && mkdir -p n8n-compose && cd n8n-compose
B) Domain not resolving / browser can’t reach site
- Double‑check the Hostinger A record points to the correct VM external IP.
- Clear local DNS cache or try a different network.
- Verify from VM:
dig +short n8n.example.com
- Ensure VM firewall allows 80/443 (we ticked these at create time).
- If you use any CDN/proxy, disable it until SSL is issued.
C) NET::ERR_CERT_AUTHORITY_INVALID or SSL didn’t issue
- DNS likely hasn’t propagated or ports 80/443 blocked by another process.
- Wait a few minutes, then check Traefik logs:
docker compose logs -f traefik
- Recreate if needed:
docker compose down && docker compose up -d
D) Blank page / 502 from proxy
- Traefik couldn’t see the n8n container or the labels are wrong.
- Confirm labels exactly match
SUBDOMAIN.DOMAIN_NAME. - Confirm n8n is listening internally on
127.0.0.1:5678. - Restart both services:
docker compose restart n8n traefik
E) n8n offline after VM reboot
- Docker not running or containers didn’t come up.
- We set
restart: always, but verify:
systemctl status docker
sudo systemctl enable docker --now
F) “Permission denied” writing to /files inside n8n
- Ensure the host directory exists (
./local-files). - Check mount is present:
docker compose exec n8n ls -la /files.
Hardening & Nice‑to‑Haves (optional)
- Lock down Traefik dashboard: We used
--api.insecure=truefor simplicity. Remove it or protect behind auth/reverse proxy in production. - Set an encryption key: Add
N8N_ENCRYPTION_KEYin theenvironment:for the n8n service (a long random string) before you create credentials. - Backups: Snapshot the VM or back up the
n8n_datavolume regularly. - Updates:
# Update base OS
sudo apt-get update && sudo apt-get upgrade -y
# Update containers
docker compose pull
docker compose up -d
Leave a Reply