1. Provision DigitalOcean droplet
Create a $10/mo “Basic” droplet running **Ubuntu 22.04 LTS** from the control-panel or via `doctl`:
2. Choose closest region + IPv6.
3. Add your existing SSH public key.
4. Name it `n8n-prod`.
After creation, note the public IP.
Log in as **root** and create a limited sudo user:
```bash
adduser deploy && usermod -aG sudo deploy
```
Harden server:
```bash
ufw allow OpenSSH && ufw allow 80,443/tcp && ufw enable
sed -i 's/^#Port 22/Port 2222/' /etc/ssh/sshd_config && systemctl restart sshd
```
Test new user/port before closing the root session.
2. Install Docker & Compose on droplet
```bash
su - deploy
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
docker --version
sudo curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker compose version # v2.x expected
```
3. Create local project skeleton (Windows)
```powershell
cd C:\Users\nucle\Projects
mkdir n8n-selfhost; cd n8n-selfhost
ni docker-compose.yml, traefik.yml, .env, .dockerignore, README.md, acme.json
```
Set empty `acme.json` to 600 later on the server. Populate `.env` placeholders:
```
DOMAIN=n8n.chron0.tech
[email protected]
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=<strongPW>
N8N_ENCRYPTION_KEY=<32-char-key>
```
4. Write docker-compose.yml
```yaml
version: '3.9'
services:
traefik:
image: traefik:v3.0
restart: unless-stopped
command:
- "--providers.docker=true"
- "--providers.docker.network=proxy"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--certificatesresolvers.le.acme.email=${EMAIL}"
- "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
ports:
- "80:80"
- "443:443"
volumes:
- "./acme.json:/letsencrypt/acme.json"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
networks:
- proxy
n8n:
image: n8nio/n8n:latest
restart: unless-stopped
environment:
- N8N_HOST=${DOMAIN}
- WEBHOOK_URL=https://${DOMAIN}/
- N8N_PROTOCOL=https
- TZ=UTC
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=${N8N_BASIC_AUTH_USER}
- N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD}
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
volumes:
- n8n_data:/home/node/.n8n
labels:
- "traefik.enable=true"
- "traefik.http.routers.n8n.rule=Host(`${DOMAIN}`)"
- "traefik.http.routers.n8n.entrypoints=websecure"
- "traefik.http.routers.n8n.tls.certresolver=le"
networks:
- proxy
volumes:
n8n_data:
networks:
proxy:
external: true
```
5. Write traefik.yml (optional file-based extras)
If you prefer static-file config instead of CLI flags, mirror the same options in `traefik.yml` and mount it.
Ensure `acme.json` gets **chmod 600** on the droplet:
```bash
chmod 600 acme.json
```
6. Configure Cloudflare DNS
Create an **A record**
```
Name: n8n
IPv4: <droplet-IP>
Proxy: DNS only (grey cloud) until SSL issued
TTL: Auto
```
Wait a few minutes; verify with
```
nslookup n8n.chron0.tech
```
7. Transfer project to droplet & prepare network
On Windows PowerShell:
```powershell
scp -P 2222 -r C:\Users\nucle\Projects\n8n-selfhost deploy@<droplet-IP>:~
```
SSH in:
```bash
docker network create proxy
cd ~/n8n-selfhost
chmod 600 acme.json
```
8. Deploy stack
```bash
docker compose pull
docker compose up -d
docker compose logs -f traefik
```
Watch for `Server contacted` & `Certificate obtained`.
9. Verify and finalize
10. Browse to https://n8n.chron0.tech – should display valid Let’s Encrypt certificate.
11. Complete n8n onboarding, set timezone, create workflows.
12. Re-enable Cloudflare **orange-cloud** (proxy) if desired; certificate is already in place.
13. Maintenance & backups
Optional:
- Add Watchtower or cron to auto-update images.
- Schedule `docker exec n8n n8n export:workflow --all` backups + volume snapshot.
- Monitor with UptimeRobot.