Self-host n8n on Ubuntu 24.04 for cross-border e-commerce

You run a storefront that sells into Vietnam but your warehouse sits in China and your supplier invoices in USD. Every morning you manually copy orders from Shopify into a spreadsheet, email the warehouse, then paste tracking numbers back. That workflow breaks the moment you get more than a handful of orders a day, and it breaks even faster when your automation tool lives in a datacenter on the other side of the planet. Self-hosting n8n on an Ubuntu 24.04 VPS in Vietnam fixes the latency problem at the source, because the server that receives the webhook is the same network your Vietnamese customers and partners already use.
Why run n8n on a Vietnam VPS for cross-border work
Cross-border e-commerce automation is mostly webhook traffic. Shopify fires an order event, your warehouse API confirms stock, your bank sends a payout notification. Every one of those events travels over the public internet, and if your n8n instance sits in Singapore or the US, each round trip adds 50 to 150 ms before you even start processing. That sounds small until a Chinese warehouse API times out at 5 seconds and your n8n workflow fails at step two.
The bigger issue is outbound IP reputation. When n8n calls a warehouse or a payment gateway, that service sees the IP address of your VPS. A clean, dedicated IPv4 in Vietnam that has never sent spam passes more API checks than a shared or recycled address. For webhook delivery specifically, the receiving service often checks the origin IP against blocklists. If your previous VPS provider gave you an IP that was used for scraping, you will chase authentication errors that have nothing to do with your code.
Hosting n8n inside Vietnam also matters when your workflow touches local systems: Vietnamese banks, domestic logistics carriers, the tax authority's APIs. Those endpoints frequently block or throttle foreign IPs. A Linux VPS with a Vietnamese IPv4 talks to them as a local service, not as an international interloper. This is the practical argument for keeping your automation close to the market you automate, and it is the reason the setup in this guide uses a Vietnam-based server.
在越南部署 n8n,用本地 IPv4 接收电商 webhook,比海外服务器更稳。
Running n8n in Vietnam with a local IPv4 for e-commerce webhooks is more stable than using an overseas server.
Prerequisites
- An Ubuntu 24.04 LTS VPS with at least 2 GB of RAM. n8n plus PostgreSQL and the n8n queue mode will fit, but 4 GB is the comfortable zone when you run several workflows.
- Root access or a user with sudo privileges.
- A domain name pointing to your VPS, because you will terminate TLS with Caddy or Nginx rather than exposing n8n on a raw port.
- Docker and Docker Compose v2 installed. If they are not set up yet, follow the standard Ubuntu install for
docker-ceand the compose plugin.
Do not run n8n as a plain node process on a public port. The editor has no built-in authentication gate that is strong enough for the internet. You will put it behind a reverse proxy with TLS and lock the editor with n8n's own user management.
Step 1, Set up the base system and Docker
Start from a fresh Ubuntu 24.04 install. Update the package index, then install the packages you need for Docker's official repository:
sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-pluginThe docker-compose-plugin package gives you the v2 docker compose command, which is what the rest of this guide uses. Verify the install:
sudo docker run --rm hello-world
docker compose version
You should see the hello-world message and a compose version line. If the second command fails, log out and back in so your user is in the docker group, or run it with sudo.
Step 2, Write the Docker Compose file for n8n with PostgreSQL
Create a directory for n8n and a compose file inside it. This example uses PostgreSQL as the database instead of SQLite, which is the right call the moment you want the queue mode or more than one webhook running at the same time:
mkdir -p ~/n8n && cd ~/n8n
nano docker-compose.yml
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: n8n
POSTGRES_PASSWORD: change_this_password
POSTGRES_DB: n8n
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n"]
interval: 10s
timeout: 5s
retries: 5
n8n:
image: docker.n8n.io/n8nio/n8n
restart: unless-stopped
ports:
- "127.0.0.1:5678:5678"
environment:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_USER: n8n
DB_POSTGRESDB_PASSWORD: change_this_password
DB_POSTGRESDB_DATABASE: n8n
N8N_HOST: n8n.example.com
N8N_PROTOCOL: https
N8N_PORT: 5678
WEBHOOK_URL: https://n8n.example.com/
GENERIC_TIMEZONE: Asia/Ho_Chi_Minh
volumes:
- n8n_data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
volumes:
postgres_data:
n8n_data:
The n8n container listens on 127.0.0.1:5678 only, so it is never exposed directly to the internet. The WEBHOOK_URL variable is what n8n uses when it registers webhooks with Shopify or other services, and it must match your public domain or the registrations will point at the wrong host. Set GENERIC_TIMEZONE to Asia/Ho_Chi_Minh so scheduling nodes fire on Vietnam time.
Start the stack and confirm both containers are healthy:
docker compose up -d
docker compose ps
You want both postgres and n8n to show a healthy or running state. If the n8n container keeps restarting, check the logs with docker compose logs n8n; the usual cause is a password mismatch in the compose file.
Step 3, Put Caddy in front for automatic HTTPS
Exposing the editor even on a high port is a bad idea. Install Caddy on the host and let it terminate TLS and forward to the local n8n port. Caddy fetches and renews certificates automatically, which removes the certbot chore from your plate:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
Point your domain's A record at the VPS IPv4 before you start Caddy, otherwise the TLS handshake will fail. Then write a minimal Caddyfile:
sudo nano /etc/caddy/Caddyfile
n8n.example.com {
reverse_proxy 127.0.0.1:5678
}
Reload Caddy and check that the certificate was issued:
sudo systemctl reload caddy
sudo journalctl -u caddy -n 20 --no-pager
You should see a log line about successfully obtaining a certificate. Open https://n8n.example.com in a browser and you will get the n8n setup screen, which is where you create the owner account.
Step 4, Open the firewall only for the ports you need
Ubuntu ships with ufw disabled by default. Enable it and allow only SSH, HTTP and HTTPS. Do not open port 5678, because Caddy is the only thing that talks to n8n and it does that over localhost:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
The status output should list the three rules and show the default policy as deny incoming. If you manage the VPS through a control panel that uses a non-standard SSH port, adjust the first rule to match it before you enable the firewall or you will lock yourself out.
Verify the whole path from the outside:
curl -I https://n8n.example.com
Expect an HTTP/2 200 header. If you get a timeout, the A record or the firewall rule is wrong; if you get a certificate error, the domain did not point here when Caddy started.
Step 5, Build a workflow for cross-border order sync
With n8n reachable over HTTPS, the real work starts. A typical cross-border flow for a Vietnam-facing storefront looks like this:
- A Shopify Trigger node fires on
orders/paid. - An HTTP Request node pushes the order to your Chinese warehouse API, mapping the Vietnamese shipping address into the fields the warehouse expects.
- A Wait node polls for the warehouse response, because fulfilment takes minutes, not milliseconds.
- An HTTP Request node sends the tracking number back to Shopify.
- An Email Send node notifies your Vietnamese customer service inbox, which is a Gmail or Outlook account that talks to customers in Vietnamese.
Keep the language mapping in a separate step. Vietnamese addresses and customer names rarely match the fields a Chinese warehouse API was built for. Add a small Code node that normalises the payload before it leaves the server:
const order = $input.first().json;
return [{
json: {
order_id: order.name,
recipient: order.shipping_address.name,
phone: order.shipping_address.phone,
province: order.shipping_address.province,
district: order.shipping_address.city,
address: `${order.shipping_address.address1} ${order.shipping_address.zip}`,
items: order.line_items.map(item => ({
sku: item.sku,
qty: item.quantity
}))
}
}];
This is where a n8n VPS setup pays for itself. The workflow runs on a schedule or a webhook without you touching it, and a failed step retries according to the error workflow you attach. Test each node individually first, then activate the workflow and place a real test order.
Step 6, Back up n8n data before you need it
n8n stores credentials and workflow JSON in the n8n_data volume, and PostgreSQL holds the execution history. Losing the volume means rebuilding every credential by hand, which nobody wants to do at 2 AM. Back up both the Postgres database and the volume with a simple script run from cron:
mkdir -p ~/backups
sudo docker compose exec -T postgres pg_dump -U n8n n8n | gzip > ~/backups/n8n_$(date +\%F).sql.gz
That dumps the database. For the n8n files themselves, copy the named volume to a tar archive:
sudo tar czf ~/backups/n8n_volume_$(date +\%F).tar.gz -C /var/lib/docker/volumes/n8n_n8n_data/_data .
Schedule both with crontab -e and a daily entry, then copy the files off the server to object storage or another host. A backup that lives on the same disk as the VPS is not a backup, it is a recovery convenience.
If you rent the VPS from a provider that offers snapshots, take one before you upgrade the n8n image. VPS snapshots on KVM give you a rollback point that works even when the application-level backup is corrupted.
Troubleshooting common n8n failures
Webhooks from Shopify never arrive. Check that WEBHOOK_URL in the compose file matches your public domain exactly, including the trailing slash. Shopify validates the endpoint URL when you register it, and a mismatch silently drops the registration. Then confirm the port is reachable: curl -I https://n8n.example.com/webhook-test/<your-path> should return a 200 from n8n, not a 404 from Caddy.
n8n runs out of memory during a big import. A 2 GB VPS is workable for a handful of workflows, but queue mode with multiple workers is not. Check usage with docker stats and if the container sits above 1.5 GB, move to a 4 GB Ubuntu VPS or enable swap. Swap is a stopgap; it will not make a 2 GB box run 10 concurrent executions smoothly.
Certificate renewal fails after a few months. Caddy renews automatically, but only if port 80 stays open, because the HTTP challenge needs it. If you closed port 80 to "be more secure", renewal breaks. Keep 80 open and let Caddy redirect to HTTPS.
FAQ
How much RAM does self-hosted n8n need on an Ubuntu VPS?
2 GB runs n8n with PostgreSQL for a small number of workflows. 4 GB is the practical minimum when you enable the queue mode or run several webhook executions per minute. Execution history grows fast, so monitor docker stats after your first week.
Why run n8n in Vietnam instead of Singapore for a cross-border business?
Because your Vietnamese customers, logistics partners and local APIs are on Vietnamese networks. A server in Vietnam with a domestic IPv4 reaches those endpoints over domestic routes, and webhook delivery from Shopify to a Vietnam IP is typically more stable than a long international hop to a distant provider.
Is PostgreSQL necessary for n8n, or is SQLite enough?
SQLite is fine for testing on a laptop. For a server that handles real orders, use PostgreSQL from day one. Migrating n8n from SQLite to Postgres later means exporting and re-importing credentials, which is unnecessary risk.
Can I self-host n8n on a Windows VPS instead?
Yes, n8n runs on Windows too, but the Docker setup in this guide is Linux-oriented and Ubuntu is the path with the least friction. If your whole stack is Windows already, a Windows VPS with Docker Desktop works, just expect slightly higher resource use.
What is the cheapest way to test n8n before committing to a full setup?
Rent a small Ubuntu VPS with 2 GB of RAM, follow this guide, and run your first workflow for a week. Monthly billing VPS plans let you cancel without losing a yearly prepayment, which suits a trial run.
Related articles
- Self-host n8n on an Ubuntu VPS with a Vietnam IPv4
- Deploy and secure n8n on a Debian VPS in Vietnam
- n8n on a dedicated IP, why webhook reliability depends on it
- How much RAM to run multiple n8n workflows
跨境电商 n8n 自托管要点
在越南 Ubuntu VPS 上自托管 n8n,用本地 IPv4 接收 Shopify 等平台的 webhook,对面向越南市场的跨境电商更稳定。建议使用 Docker Compose 搭配 PostgreSQL,并用 Caddy 自动配置 HTTPS,不要把 n8n 直接暴露到公网。工作流应处理越南地址与中文仓库 API 的字段映射,并每日备份数据库与数据卷。2GB 内存可跑少量流程,生产环境建议 4GB。


