# Self-hosting

> Run Runnel Server on your own infrastructure with Docker and Postgres, point your workers at it and keep it upgraded.

Source: https://galley.themes.dev/docs/guides/self-hosting/

Runnel Server is the same queue, scheduler, API and dashboard that runs Runnel Cloud, packaged as one Docker image. It is open source under the Apache 2.0 licence. Teams self-host when their data must stay in their own network or region; for everything else, the cloud is less work. The SDKs, CLI and API are identical in both, so you can move between them without changing your jobs.

## What you need

- Docker, or any platform that runs OCI images. The image is `runnel/server:2.8`, about 60 MB.
- PostgreSQL 15 or newer. We recommend 17, which Runnel Server has used in its own tests since 2.7.
- About 1 vCPU and 1 GB of memory per 2,000 runs a minute. The database does most of the work; give it fast disks.
- A domain with TLS in front of the server, for example `runnel.internal.example`.

## Starting with Docker Compose

This is a complete setup for a trial or a small team. For production, use a managed Postgres with backups instead of the container.

```yaml title="docker-compose.yml"
services:
  runnel:
    image: runnel/server:2.8
    ports: ["8288:8288"]
    environment:
      DATABASE_URL: postgres://runnel:${POSTGRES_PASSWORD}@db:5432/runnel
      RUNNEL_SECRET_KEY: ${RUNNEL_SECRET_KEY}
      RUNNEL_PUBLIC_URL: https://runnel.internal.example
    depends_on: [db]
  db:
    image: postgres:17
    environment:
      POSTGRES_USER: runnel
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes: ["pgdata:/var/lib/postgresql/data"]
volumes:
  pgdata:
```

```bash
export RUNNEL_SECRET_KEY=$(openssl rand -hex 32) POSTGRES_PASSWORD=$(openssl rand -hex 16)
docker compose up -d
docker compose exec runnel runnel-server admin create --email ops@yourcompany.example
```

The server runs its database migrations on start. The last command prints a one-time sign-in link for the first admin.

## Configuration

| Variable | Required | Description |
|---|---|---|
| `DATABASE_URL` | yes | A Postgres connection string. The user needs to create tables in its database. |
| `RUNNEL_SECRET_KEY` | yes | 32 random bytes as hex. Encrypts webhook secrets and signs sessions. Keep it safe and never change it casually. |
| `RUNNEL_PUBLIC_URL` | yes | The address workers, the CLI and browsers use. Used in links and webhook payloads. |
| `RUNNEL_LOG_RETENTION` | no | How long run logs are kept. Default `'30d'`. |
| `RUNNEL_SMTP_URL` | no | For invitations and failure emails. Without it, invitations are shown as links. |

> **Warning** Losing `RUNNEL_SECRET_KEY` means every webhook endpoint secret has to be rotated and every user signs in again. Store it with the database credentials.

## Pointing workers and the CLI at your server

Set `RUNNEL_URL` wherever you set `RUNNEL_API_KEY`. Everything else stays the same.

```bash
export RUNNEL_URL=https://runnel.internal.example
runnel login
runnel deploy --env production
```

Workers connect out to the server, so they can run in another network as long as they can reach `RUNNEL_PUBLIC_URL` over HTTPS. The server never connects to the workers, except for push endpoints if you use [serverless functions](/docs/guides/deploying/).

## Upgrading and backups

Minor versions (2.7 to 2.8) can be upgraded in place: change the image tag and restart. Migrations are backwards compatible within a major version, so you can roll back the image if something looks wrong. Read the [changelog](/changelog/) before each upgrade; anything that needs a step from you is listed under "Upgrading".

Back up Postgres the way you back up any other production database. The database is the only state; the server containers can be replaced at any time, and you can run two or more behind a load balancer for availability.

## Support

The community helps on Discord and GitHub Discussions, and we answer there too. The Enterprise plan adds support for self-hosted installations: a private channel with our engineers, a four-hour response time for production incidents, upgrade reviews and help with sizing. See [pricing](/pricing/) or write to sales@runnel.example.
