> ## Documentation Index
> Fetch the complete documentation index at: https://docs.arct.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Self-Host n8n on a VPS

> Install n8n on an Arct Cloud Linux VPS with Docker Compose, HTTPS, persistent data, backups, and safe updates.

Arct Cloud provides an unmanaged Linux VPS. n8n is not preinstalled or managed by Arct Cloud; you are responsible for installation, security, backups, updates, and workflow behavior.

This guide uses n8n's recommended Docker Compose deployment with Traefik on Ubuntu 24.04. Docker officially supports Ubuntu 24.04.

## Requirements

| Basis                             | CPU and memory                                                                                                                           | Software and storage                                                                                                                          |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| **n8n requirements**              | n8n does not publish a fixed numeric CPU or RAM minimum; requirements depend on workflow concurrency and the data each execution handles | A Linux host with Docker Engine and Docker Compose; persistent storage for `/home/node/.n8n`                                                  |
| **Practical Arct recommendation** | 2 vCPUs and 4 GB RAM (`cvm.micro`)                                                                                                       | 40 GB NVMe for a small production instance; increase RAM and storage for concurrent workflows, large binary data, or long execution retention |

<Note>n8n is usually more sensitive to memory than CPU. Monitor actual workflow usage before increasing concurrency or adding queue-mode workers.</Note>

## Install n8n

<Steps>
  <Step title="Deploy Ubuntu 24.04">
    [Deploy a server](/compute/virtual-machines/deploy), choose a plan that fits the workload, and select Ubuntu 24.04.
  </Step>

  <Step title="Connect over SSH">
    Find the server IP address in the Arct Cloud console, then connect:

    ```bash theme={null}
    ssh ubuntu@YOUR_SERVER_IP
    ```

    See [Connect via SSH](/compute/virtual-machines/connect-ssh) for key and username help.
  </Step>

  <Step title="Point a domain to the server">
    Create an `A` record such as `n8n.example.com` that points to the server's public IPv4 address. Add an `AAAA` record only when IPv6 is configured and reachable on the VPS.

    Wait for the record to resolve to this server before starting Traefik:

    ```bash theme={null}
    getent ahostsv4 n8n.example.com
    ```
  </Step>

  <Step title="Install Docker Engine and Compose">
    Install Docker from Docker's official Ubuntu repository:

    ```bash theme={null}
    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/ubuntu/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 >/dev/null <<EOF
    Types: deb
    URIs: https://download.docker.com/linux/ubuntu
    Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
    Components: stable
    Architectures: $(dpkg --print-architecture)
    Signed-By: /etc/apt/keyrings/docker.asc
    EOF

    sudo apt update
    sudo apt install -y docker-ce docker-ce-cli containerd.io \
      docker-buildx-plugin docker-compose-plugin
    sudo systemctl enable --now docker
    sudo docker run --rm hello-world
    ```
  </Step>

  <Step title="Create the n8n configuration">
    Create a protected project directory, generate an encryption key, and create temporary proxy credentials for the first-owner setup:

    ```bash theme={null}
    sudo apt update
    sudo apt install -y apache2-utils
    sudo install -d -m 750 -o "$USER" -g "$USER" /opt/n8n
    cd /opt/n8n
    openssl rand -hex 32
    htpasswd -nB setup > setup-users
    chmod 600 setup-users
    ```

    `htpasswd` prompts for a temporary password without echoing it. Save that password until the owner account exists. Copy the generated encryption-key value, then create `.env`, replacing every example value:

    ```bash theme={null}
    tee .env >/dev/null <<'EOF'
    DOMAIN_NAME=example.com
    SUBDOMAIN=n8n
    GENERIC_TIMEZONE=Europe/Istanbul
    SSL_EMAIL=admin@example.com
    N8N_ENCRYPTION_KEY=PASTE_THE_GENERATED_VALUE_HERE
    EOF
    chmod 600 .env
    mkdir -p local-files
    ```

    `N8N_ENCRYPTION_KEY` encrypts stored credentials. Keep a secure copy outside the VPS; an n8n database backup is not useful for encrypted credentials without the matching key.
  </Step>

  <Step title="Create the Docker Compose project">
    This Compose file follows n8n's official Traefik setup. It exposes only ports `80` and `443`; n8n port `5678` is bound to loopback.

    ```bash theme={null}
    tee compose.yaml >/dev/null <<'EOF'
    services:
      traefik:
        image: traefik
        restart: always
        command:
          - --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
          - ./setup-users:/etc/traefik/setup-users:ro

      n8n:
        image: docker.n8n.io/n8nio/n8n
        restart: always
        ports:
          - "127.0.0.1:5678:5678"
        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-setup-auth.basicauth.usersfile=/etc/traefik/setup-users
          - traefik.http.routers.n8n.middlewares=n8n-setup-auth
        environment:
          - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
          - N8N_HOST=${SUBDOMAIN}.${DOMAIN_NAME}
          - N8N_PORT=5678
          - N8N_PROTOCOL=https
          - N8N_WEBHOOK_URL=https://${SUBDOMAIN}.${DOMAIN_NAME}/
          - N8N_PROXY_HOPS=1
          - NODE_ENV=production
          - GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
          - TZ=${GENERIC_TIMEZONE}
          - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
          - N8N_RESTRICT_FILE_ACCESS_TO=/files
        volumes:
          - n8n_data:/home/node/.n8n
          - ./local-files:/files

    volumes:
      n8n_data:
        name: n8n_data
      traefik_data:
        name: n8n_traefik_data
    EOF

    sudo docker compose config
    ```

    <Warning>`docker compose config` expands the environment file and can print the encryption key. Do not paste its output into tickets, chat, or public logs.</Warning>
  </Step>

  <Step title="Create the owner behind temporary authentication">
    Start the containers, then confirm an unauthenticated request receives `401` from Traefik:

    ```bash theme={null}
    cd /opt/n8n
    sudo docker compose up -d
    curl -sS -o /dev/null -w '%{http_code}\n' https://n8n.example.com
    ```

    Do not continue unless the last command returns `401`. Open `https://n8n.example.com`, enter the temporary `setup` proxy credentials, create the instance owner with a different unique password, and enable two-factor authentication in personal settings. Invite only trusted users; workflow nodes can access credentials and make network requests.

    After confirming that the owner can sign in with MFA, remove these three temporary lines from `compose.yaml`:

    ```yaml theme={null}
    - ./setup-users:/etc/traefik/setup-users:ro
    - traefik.http.middlewares.n8n-setup-auth.basicauth.usersfile=/etc/traefik/setup-users
    - traefik.http.routers.n8n.middlewares=n8n-setup-auth
    ```

    Reconcile the stack and verify the proxy prompt is gone while the n8n login remains:

    ```bash theme={null}
    cd /opt/n8n
    sudo docker compose up -d
    curl -sS -o /dev/null -w '%{http_code}\n' https://n8n.example.com
    ```

    The response must no longer be `401`. After confirming the n8n login page appears instead of the owner-creation screen, delete the temporary proxy credential file:

    ```bash theme={null}
    rm -f /opt/n8n/setup-users
    ```

    If n8n shows its owner-creation screen again, stop and inspect the persistent `n8n_data` volume before exposing the route.
  </Step>

  <Step title="Verify the deployment">
    Confirm that both containers are running, n8n is healthy locally, and the public HTTPS endpoint responds:

    ```bash theme={null}
    cd /opt/n8n
    sudo docker compose ps
    curl -fsS http://127.0.0.1:5678/healthz
    curl -I https://n8n.example.com
    sudo docker compose logs --tail 50 n8n
    ```

    Create a manual test workflow, run it once, and confirm that a webhook node displays the public `https://n8n.example.com/` URL.
  </Step>
</Steps>

## Data, Secrets, and Network Security

| Item                                                            | Location                                              | Backup requirement                                                             |
| --------------------------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------ |
| n8n database, credentials, settings, and generated key metadata | Docker volume `n8n_data` mounted at `/home/node/.n8n` | Back up consistently; do not delete the volume during upgrades                 |
| Explicit credential encryption key and domain settings          | `/opt/n8n/.env`                                       | Store an encrypted copy separately from the VPS                                |
| Files used by file-processing nodes                             | `/opt/n8n/local-files` mounted at `/files`            | Back up if workflows depend on them                                            |
| TLS certificate state                                           | Docker volume `n8n_traefik_data`                      | Optional to restore; Traefik can request a new certificate when DNS is correct |

Only SSH, HTTP, and HTTPS need inbound access. Restrict SSH to trusted source addresses where possible. Do not publish port `5678`; the loopback mapping is for local health checks only.

<Warning>Docker can bypass ordinary UFW rules for published container ports. The Compose file publishes only `80` and `443` publicly. Review any future `ports` additions and never expose databases, task runners, or the Docker socket to the internet.</Warning>

n8n stores secrets used by workflows. Use least-privilege API credentials, review community nodes before installation, prune unnecessary execution data, and run n8n's built-in security audit after initial setup and major configuration changes.

## Back Up and Restore

Create portable workflow and encrypted-credential exports regularly:

```bash theme={null}
cd /opt/n8n
mkdir -p local-files/backups/workflows local-files/backups/credentials
sudo docker compose exec -T n8n n8n export:workflow \
  --backup --output=/files/backups/workflows/
sudo docker compose exec -T n8n n8n export:credentials \
  --backup --output=/files/backups/credentials/
```

These exports do not include users, settings, execution history, or every file. For a complete SQLite deployment backup, stop n8n briefly and copy its named volume:

```bash theme={null}
cd /opt/n8n
sudo install -d -m 700 backups
sudo docker compose stop n8n
sudo docker run --rm \
  -v n8n_data:/source:ro \
  -v /opt/n8n/backups:/backup \
  alpine:3 sh -c 'tar -C /source -czf /backup/n8n-data.tgz .'
sudo docker compose start n8n
sudo tar -czf backups/n8n-config.tgz compose.yaml .env local-files
```

Encrypt the archives and copy them off the VPS. Test recovery on a separate server: restore `n8n-data.tgz` into a new empty volume, restore the exact `.env` encryption key and Compose file, start the same n8n version used for the backup, then verify login, credentials, workflows, and webhooks before updating.

<Note>Never export credentials with `--decrypted` into routine backups. The default encrypted export is safer, provided you preserve `N8N_ENCRYPTION_KEY` separately.</Note>

## Update and Roll Back Safely

1. Read the [n8n release notes](https://docs.n8n.io/release-notes/) for breaking changes and update at least monthly rather than skipping many releases.

2. Record the running version and image digest:

   ```bash theme={null}
   cd /opt/n8n
   sudo docker compose exec -T n8n n8n --version
   sudo docker image inspect docker.n8n.io/n8nio/n8n \
     --format '{{index .RepoDigests 0}}'
   ```

3. Create and verify a full backup, including `.env` and `n8n_data`.

4. Pull the current stable image and recreate the containers:

   ```bash theme={null}
   cd /opt/n8n
   sudo docker compose pull
   sudo docker compose up -d
   sudo docker compose ps
   sudo docker compose logs --tail 100 n8n
   ```

5. Verify login, credentials, a manual workflow, and a public webhook before pruning old images.

Database migrations can make an older image incompatible with upgraded data. For rollback, restore the pre-update volume backup into a new empty volume and run the exact previous n8n image tag recorded before the change. Do not point an older container at the already-migrated volume and hope it will reverse migrations.

## Troubleshooting

<AccordionGroup>
  <Accordion title="Traefik returns 404 or the certificate is not issued">
    Confirm the `A` record resolves to this VPS and ports `80` and `443` are reachable. Check `sudo docker compose logs --tail 100 traefik`. Remove a stale `AAAA` record if IPv6 does not reach the server.
  </Accordion>

  <Accordion title="The page returns 502 Bad Gateway">
    Run `sudo docker compose -f /opt/n8n/compose.yaml ps` and inspect `sudo docker compose -f /opt/n8n/compose.yaml logs --tail 100 n8n`. Confirm `curl -fsS http://127.0.0.1:5678/healthz` succeeds.
  </Accordion>

  <Accordion title="Webhook URLs use localhost or the wrong scheme">
    Confirm `N8N_WEBHOOK_URL=https://.../` and `N8N_PROXY_HOPS=1` are present in the rendered container environment, then recreate n8n with `sudo docker compose up -d`. Avoid the deprecated `WEBHOOK_URL` variable.
  </Accordion>

  <Accordion title="n8n restarts or workflows fail with out-of-memory errors">
    Check `sudo docker stats --no-stream`, reduce workflow concurrency or binary-data size, and review execution-data retention. Resize the VPS before adding workers or memory-heavy AI and file-processing workflows.
  </Accordion>

  <Accordion title="Credentials cannot be decrypted after a restore">
    Stop n8n and restore the exact `N8N_ENCRYPTION_KEY` used by the backup. Recreating a key cannot decrypt existing credential records.
  </Accordion>
</AccordionGroup>

## Official Resources

<CardGroup cols={3}>
  <Card title="n8n Docker Compose Guide" icon="book-open" href="https://docs.n8n.io/deploy/host-n8n/install-options/use-a-cloud-provider/use-docker-compose/">
    Official Docker Compose, DNS, Traefik, and HTTPS setup.
  </Card>

  <Card title="n8n on GitHub" icon="github" href="https://github.com/n8n-io/n8n">
    Source code, security policy, and issue tracker.
  </Card>

  <Card title="n8n Releases" icon="tag" href="https://github.com/n8n-io/n8n/releases">
    Stable releases, fixes, and upgrade notes.
  </Card>
</CardGroup>

<Note>n8n is developed by n8n GmbH. Arct Cloud is an independent infrastructure provider and is not affiliated with, sponsored by, or endorsed by n8n GmbH.</Note>
