Running n8n on Google Cloud’s free-tier VM is a popular way to get a self-hosted automation server for very little money. It works, but with caveats that older guides (including the first version of this one) glossed over. This walkthrough sets up n8n behind Nginx with a free Let’s Encrypt certificate on a Compute Engine e2-micro VM, using Docker Compose, and is upfront about the real cost and the limits of that machine.
Updated October 2026: I rewrote this guide against Google’s and n8n’s current documentation. Main changes: the free tier is not entirely free (the VM’s public IPv4 address costs about $3.65 per month); the Docker install now uses Docker’s official repository and Compose v2 rather than the old docker-compose package; the configuration uses N8N_PROXY_HOPS and the current webhook variable instead of the development-tunnel setting; the n8n container is no longer exposed directly to the internet; and the “enterprise” Postgres, queue-mode and monitoring sections are gone because they don’t fit on a 1 GB machine. I wrote this from the vendors’ documentation and validated the Nginx and Compose files for syntax, but I did not run the whole deployment on a fresh VM for this update, so test it on a throwaway instance first.
What This Costs, Honestly
The first version of this post said n8n could run on Google Cloud with no monthly cost. Here is what Google’s own pages say:
- The VM is free within limits. Always Free includes one non-preemptible
e2-microinstance per month inus-west1(Oregon),us-central1(Iowa) orus-east1(South Carolina), 30 GB-months of standard persistent disk, and 1 GB of outbound data transfer per month from North America. The VM allowance is measured in hours, equal to the hours in the month, so one VM running continuously fits. - The public IP address is not free. Google’s network pricing page lists static and ephemeral external IPv4 addresses in use on standard VM instances at $0.005 per hour, with a free allowance of only one hour per month. Running continuously (about 730 hours), that is roughly $3.65 per month. A static address is counted as in use while it is attached to a VM, running or stopped.
- Outbound traffic over 1 GB is billed, and so is any disk beyond 30 GB or extra snapshots. An automation server that pulls and pushes data can pass 1 GB, so check your billing reports in the first month.
- The free tier still needs a billing account on your project, so set a budget alert before you start.
So the realistic cost is a few dollars a month, not zero. That is still cheap, but a lot less “free” than the original headline suggested.
Is an e2-micro Enough for n8n?
Be realistic about the machine. According to Google’s documentation, the e2-micro is a shared-core type with 2 vCPUs that sustain 25% of CPU time in total (12.5% each) and can burst higher for short periods, with 1 GB of memory. (The original post said “1 vCPU”, which was wrong.) n8n’s own docs do not publish a minimum RAM figure for a basic install, but its guide for a fuller Docker Compose stack asks for at least 4 GB of RAM and 2 vCPUs, so treat 1 GB as a small-scale, learning or hobby setup rather than a production host.
What that means in practice: a handful of lightweight workflows (schedule triggers, webhooks, API calls, small data) is fine. Workflows that process large files or many items at once will run out of memory. n8n’s memory-troubleshooting page lists the symptoms (“Allocation failed – JavaScript heap out of memory”, “Connection Lost”, 503 errors) and the main fix, which is to process data in smaller batches. If you hit those regularly, move to a larger machine or to n8n Cloud, rather than tuning the free VM. We add a swap file below as a safety net, but it is a safety net, not extra capacity.
Skip the heavy extras. The earlier version of this post piled on PostgreSQL, Redis and queue mode. Each of those needs more memory than the VM has. This setup uses n8n’s default SQLite database, which n8n’s documentation describes as fine for trying things out, and recommends replacing with PostgreSQL for production use that needs to run around the clock with more than a handful of users or workflows.
The Architecture
Internet traffic reaches Nginx on ports 80 and 443. Nginx terminates HTTPS using a Let’s Encrypt certificate (issued and renewed by Certbot) and forwards requests to the n8n container, which listens only on 127.0.0.1:5678, so it is unreachable except through Nginx. n8n’s data lives in a Docker volume. Docker Compose describes the container, and it restarts automatically after a reboot.
About n8n’s licence: n8n is “fair-code”, not open source in the OSI sense. Its source is available under the Sustainable Use License, which lets you use it free of charge for your own internal business purposes or personal use, but restricts selling it or offering it to others as a product. For a personal or company-internal automation server that is fine; if you plan to resell access, read the licence FAQ first.
Prerequisites
- A Google Cloud account with billing enabled, and a budget alert set.
- A domain you control, where you can add a DNS record (for example,
n8n.yourdomain.com). - Basic comfort with a Linux terminal.
Step 1: Create the VM
In the Google Cloud console, create a project (it helps to keep costs separated), then go to Compute Engine > VM instances > Create instance and choose:
- Region:
us-west1,us-central1orus-east1(the Always Free regions). - Machine type:
e2-micro(E2 series). The original post’s configuration block saide2-mediumin one place; that machine is not in the free tier. - Boot disk: Debian, current stable version (Debian 13 and 12 are both supported by Docker’s official instructions), standard persistent disk, 30 GB or less.
- Firewall: tick Allow HTTP traffic and Allow HTTPS traffic.
Then give the VM a stable address: VPC network > IP addresses > Reserve external static IP address, in the same region, and attach it to the VM. A stable address matters because your DNS record will point at it. The cost described above applies to the address whether static or ephemeral.
Step 2: Point Your Domain at the VM
At your DNS provider, create an A record for your subdomain pointing to the VM’s external IP (a low TTL such as 300 seconds is convenient while setting up). Check propagation from your computer:
nslookup n8n.example.com
Wait until it returns your VM’s IP before requesting a certificate in Step 6; otherwise Let’s Encrypt cannot validate the domain.
Step 3: Prepare the Server
Connect with the SSH button in the console, then update the system and add a 2 GB swap file, which gives the 1 GB machine some breathing room when memory spikes:
sudo apt update && sudo apt upgrade -y
# 2 GB swap file (safety net for a 1 GB machine)
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -h
The swap file lives on the boot disk, which counts against your 30 GB allowance. Swapping to disk is slow, so frequent heavy use of swap means you need more RAM, not a bigger swap file.
Step 4: Install Docker from the Official Repository
Debian’s own docker.io and docker-compose packages are older and conflict with Docker’s current packages; the original post installed them. Docker’s official Debian instructions use Docker’s own apt repository, which gives you Docker Engine and Compose v2 (run as docker compose, with a space). These are the commands from that page:
# Remove any conflicting unofficial packages (it is fine if none are installed)
sudo apt remove $(dpkg --get-selections docker.io docker-compose docker-doc docker-buildx podman-docker containerd runc | cut -f1)
# Add Docker's repository
sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/debian
Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt update
# Install Docker Engine and the Compose plugin
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo docker run hello-world
docker compose version
The Docker packages enable and start the service automatically, so the separate systemctl enable step in the earlier version is not needed. If docker compose version fails, the plugin did not install; fix that before continuing.
Step 5: Run n8n with Docker Compose
Create a project folder, a .env file and a compose.yaml:
mkdir -p ~/n8n && cd ~/n8n
nano .env
Put this in .env, replacing the domain and timezone. For the version, use the current stable release number from the n8n releases page (n8n’s docs listed 2.41.5 as stable when I wrote this) rather than latest, so updates happen when you decide, not by accident:
N8N_VERSION=2.41.5
N8N_DOMAIN=n8n.example.com
GENERIC_TIMEZONE=America/New_York
nano compose.yaml
services:
n8n:
image: n8nio/n8n:${N8N_VERSION}
container_name: n8n
restart: unless-stopped
ports:
# Listen on localhost only: nginx is the only way in from the internet
- "127.0.0.1:5678:5678"
environment:
- N8N_HOST=${N8N_DOMAIN}
- N8N_PORT=5678
- N8N_PROTOCOL=https
- N8N_WEBHOOK_URL=https://${N8N_DOMAIN}/
- N8N_PROXY_HOPS=1
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- TZ=${GENERIC_TIMEZONE}
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
volumes:
- n8n_data:/home/node/.n8n
volumes:
n8n_data:
# fixed name so the backup commands below can refer to it
name: n8n_data
What the settings do:
127.0.0.1:5678:5678publishes the port to the VM itself only. The old version published5678on all interfaces, which, combined with permissive firewall rules, risked exposing n8n over plain HTTP.N8N_HOST,N8N_PORTandN8N_PROTOCOLtell n8n how it is reached;N8N_WEBHOOK_URLis the public base URL it shows and registers for webhooks. The n8n reverse-proxy guide says to set it, and notes that it replacesWEBHOOK_URLfrom n8n 2.35.0 (the old name still works but logs a deprecation warning). If you pin an older n8n version, useWEBHOOK_URLinstead. The oldWEBHOOK_TUNNEL_URLin the first version of this post belongs to n8n’s development tunnel and should not be used here.N8N_PROXY_HOPS=1tells n8n it is behind one reverse proxy, as the same guide requires, so it trusts the forwarded headers Nginx sends.GENERIC_TIMEZONEsets the timezone for schedule nodes, andTZsets the container’s system timezone.N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=truemakes n8n restrict its settings file (which stores the credential encryption key) to its owner, as in n8n’s Docker examples.- The named volume
n8n_data(given a fixed name so the backup command can find it) persists everything under/home/node/.n8n, including the SQLite database and the encryption key.
Start it and check that it is healthy:
docker compose up -d
docker compose ps
curl -sf http://127.0.0.1:5678/healthz && echo " n8n is up"
If you get a permissions error talking to Docker, prefix the commands with sudo, or add your user to the docker group as described in n8n’s Docker Compose guide.
Step 6: Nginx and the HTTPS Certificate
Install Nginx and Certbot:
sudo apt install -y nginx certbot python3-certbot-nginx
Create the site configuration, replacing n8n.example.com with your domain:
sudo nano /etc/nginx/sites-available/n8n.conf
# Used by the WebSocket upgrade header below
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
listen [::]:80;
server_name n8n.example.com;
# n8n accepts payloads up to 16 MiB by default (N8N_PAYLOAD_SIZE_MAX)
client_max_body_size 16m;
location / {
proxy_pass http://127.0.0.1:5678;
proxy_http_version 1.1;
# WebSocket support for the editor's live updates
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# Tell n8n about the original request (needed with N8N_PROXY_HOPS=1)
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
# Don't buffer streaming responses; allow slow workflows
proxy_buffering off;
proxy_read_timeout 300s;
}
}
I checked this file with nginx -t (syntax OK). What it does: the map block sets the WebSocket upgrade header correctly; client_max_body_size 16m matches n8n’s default 16 MiB payload limit (Nginx’s own default is 1 MB, which would reject larger webhook payloads); the X-Forwarded-* headers pass the original request details to n8n, which the proxy guide requires; and the timeout is 5 minutes rather than the 24 hours in the original, which keeps stuck connections from lingering. If you raise N8N_PAYLOAD_SIZE_MAX in n8n, raise the Nginx limit to match.
Enable the site, test and reload:
sudo ln -s /etc/nginx/sites-available/n8n.conf /etc/nginx/sites-enabled/n8n.conf
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx
Now request the certificate. Certbot edits the Nginx config to add HTTPS and the HTTP-to-HTTPS redirect:
sudo certbot --nginx -d n8n.example.com
It asks for an email address (for expiry notices) and to accept the Let’s Encrypt terms. The Debian package sets up automatic renewal; confirm it with a dry run:
sudo certbot renew --dry-run
systemctl list-timers | grep -i certbot
Certbot’s own instructions at certbot.eff.org are the authority if anything here differs on your distribution version.
Step 7: First Login and Basic Hardening
- Open
https://n8n.example.com. Create the owner account immediately. Until you do, anyone who finds the URL could create it first. - Use a strong, unique password and turn on two-factor authentication in your n8n user settings.
- Keep only ports 80 and 443 open to the world. Google Cloud’s default SSH rule is open to all addresses; restrict it to your own IP if you can.
- Read n8n’s security documentation for options such as blocking nodes you don’t use, and be careful with workflows that expose webhooks: a public webhook URL is public, so validate what it receives.
- Back up the encryption key (see Step 8). Without it, a restored database cannot decrypt your credentials.
Step 8: Backups and Updates
Backups
n8n’s backup and restore documentation says a complete backup of a Docker install is the .n8n folder (in the n8n_data volume, holding the SQLite database and the config file with the credential encryption key), and it warns that with SQLite you should stop n8n before copying the folder, since copying while n8n is writing can produce an inconsistent backup. A simple routine:
cd ~/n8n
mkdir -p ~/n8n-backups
docker compose stop n8n
docker run --rm -v n8n_data:/data -v ~/n8n-backups:/backup alpine \
tar czf /backup/n8n-data-$(date +%F).tar.gz -C /data .
docker compose start n8n
ls -lh ~/n8n-backups
Copy those archives off the VM (for example to a Cloud Storage bucket or your own computer); a backup that lives only on the machine that fails is not a backup. The same documentation page explains the CLI exports (n8n export:workflow --backup and export:credentials --backup), which are handy for moving workflows but do not include users, execution history or the encryption key on their own.
Updating n8n
n8n releases a new minor version most weeks, and its update guidance is to update frequently (at least monthly), read the release notes for breaking changes, and test first if you can. With the pinned version in .env:
cd ~/n8n
# 1. Back up first (see above)
# 2. Edit .env and set N8N_VERSION to the new stable release number
nano .env
# 3. Pull and restart
docker compose pull
docker compose down
docker compose up -d
docker compose logs --tail=50 n8n
This matches the pull, down and up sequence in n8n’s Docker update instructions. To roll back, set N8N_VERSION to the previous version and run the same commands; if the newer version migrated the database, restore your backup instead.
Keeping the system itself updated
sudo apt update && sudo apt upgrade -y
sudo docker image prune -f # removes dangling images left by updates
n8n also prunes old execution data by default (the documented defaults keep executions for 336 hours, which is 14 days, and cap them at 10,000), which helps keep a small disk from filling.
Troubleshooting
| Symptom | What to check |
|---|---|
| Certbot cannot validate the domain | DNS A record not pointing at the VM yet (nslookup); firewall rules for HTTP/HTTPS missing; Nginx not running (sudo systemctl status nginx). |
| 502 Bad Gateway from Nginx | n8n container not running or not healthy: docker compose ps, docker compose logs n8n, then curl http://127.0.0.1:5678/healthz. |
| Editor loads but live updates or the execution view fail | WebSocket headers missing from the Nginx config, or N8N_PROXY_HOPS not set to 1. |
Webhook URLs show localhost or the wrong address | N8N_WEBHOOK_URL (or WEBHOOK_URL on older versions) missing or wrong; recreate the container after changing it with docker compose up -d. |
| Large webhook payloads rejected (413) | Raise client_max_body_size in Nginx and N8N_PAYLOAD_SIZE_MAX in n8n together. |
| n8n crashes, “heap out of memory”, or the VM becomes unresponsive | The 1 GB machine is out of memory. Process less data per execution, check free -h, or move to a larger machine. |
| Container does not start after a reboot | Check docker compose ps and that the Docker service is running: sudo systemctl status docker. |
When to Move Beyond This Setup
This is a good learning and small-workload setup. Move on when workflows regularly hit memory limits, when you need more than a couple of users, or when downtime would hurt. At that point, the next steps are a larger VM with PostgreSQL (n8n’s docs show how to add it to Docker Compose) and, for heavier loads, queue mode with Redis and workers. n8n also documents deploying on Google Cloud Run and Google Kubernetes Engine, and offers n8n Cloud if you would rather not run servers at all. For related n8n hosting choices, see the best options to deploy and self-host n8n, and for what to build once it is running, an n8n RAG assistant.
Is it really free to run n8n on Google Cloud?
Not entirely. The e2-micro VM and 30 GB of standard disk fit Google’s Always Free tier in three US regions, but the VM’s public IPv4 address costs $0.005 per hour (about $3.65 per month) and outbound traffic beyond 1 GB per month is billed. Expect a few dollars a month.
Is an e2-micro big enough for n8n?
For a few light workflows, yes. The e2-micro has 1 GB of memory and 25% sustained CPU, so workflows that handle large files or many items will run out of memory. Add a swap file as a safety net, process data in smaller batches, and move to a larger machine or n8n Cloud if you hit limits.
Why is the n8n container bound to 127.0.0.1?
So that the only way to reach n8n from the internet is through Nginx with HTTPS. Publishing port 5678 on all interfaces would also expose the unencrypted n8n port if your firewall allows it.
How do I update n8n on Docker Compose?
Back up the n8n_data volume, change N8N_VERSION in your .env file to the new stable version, then run docker compose pull, docker compose down and docker compose up -d. Read the release notes for breaking changes first.
What do I need to back up?
The n8n_data volume, which holds the SQLite database and the config file with the credential encryption key. With SQLite, stop n8n before copying it. CLI exports of workflows and credentials are useful too but do not include users, execution history or the encryption key.
Is n8n open source?
n8n is fair-code: the source is available under the Sustainable Use License, which allows free use for your own internal business purposes or personal use but restricts selling it or hosting it for others as a product. It is not an OSI-approved open-source licence.