One command, your own server

Install DevGrail

Run this on a fresh VPS as root. It installs Docker, verifies and loads the DevGrail images, and brings the stack up behind Traefik with automatic HTTPS.

$ curl -fsSL https://web-dev.trixibot.com/install.sh | sudo bash

What the installer does

Detects & prepares the host
Resolves your CPU architecture (amd64 / arm64), installs Docker and jq if they're missing, and preflights Docker userns-remap so untrusted workspace code stays isolated.
Downloads verified images
Pulls the DevGrail image tarballs straight from this site — no GitHub, no docker pull, no registry auth. Each tarball is checked against its published SHA-256, then loaded with docker load.
Lays down the stack
Writes the Docker Compose and Traefik configuration, generates secrets, and brings everything up behind Traefik with Let's Encrypt HTTPS.
Upgrades in place
Re-running the same command only re-downloads images whose published version changed. Existing secrets and your data volume are preserved.

Recommended system requirements

DevGrail runs as a single-host Docker Compose stack (server, Traefik, and a docker-socket proxy) plus one isolated container per workspace. These are sensible starting points — size up with the number of concurrent workspaces you expect.

64-bit Linux

amd64 or arm64

A modern distro with systemd (Ubuntu 22.04+, Debian 12+). The installer detects your architecture and installs Docker if it's missing.
CPU

2 vCPU (4+ recommended)

The server and Traefik are light. Workspaces are uncapped unless you set a per-container CPU limit, so size cores for the workloads your agents run.
Memory

4 GB RAM (8 GB+ recommended)

2 GB is enough to boot the stack. Workspaces have no memory cap unless one is set — budget generous headroom per active session.
Storage

20 GB+ SSD

The DevGrail images are ~1–2 GB combined; the rest is Docker layers, workspace volumes, and your data. Scale with the number of workspaces.

Before you start

  • A fresh VPS you can run as root (the installer needs sudo/root).
  • A DNS A record for your dashboard hostname pointing at the VPS.
  • A wildcard DNS A record (*.<base domain>) for per-app subdomains.
  • Ports 80 and 443 reachable — real 80/443 are required for Let's Encrypt.

Configuration

Answer the prompts, or pass any of these ahead of time as environment variables:

DEVGRAIL_DOMAIN
Dashboard / API / MCP hostname (prompted if unset).
DEVGRAIL_BASE_DOMAIN
Base domain for app subdomains (prompted if unset).
ACME_EMAIL
Contact email for Let's Encrypt certificates (prompted if unset).
DEVGRAIL_WEB_URL
Site serving images + assets. Defaults to wherever the script was downloaded from — https://web-dev.trixibot.com for the command above.
HTTP_PORT / HTTPS_PORT
Host ports. Default 80 / 443 (real 80/443 needed for ACME).
DEVGRAIL_TLS
Set to off to serve plain HTTP and skip Let's Encrypt — for a private network or an existing TLS terminator in front. Default on.

Command-line flags

Flags go after -s --, which is how you pass arguments to a script that arrives on standard input:

$ curl -fsSL https://web-dev.trixibot.com/install.sh | sudo bash -s -- --dry-run
--dry-run
Report what the run would change — release, image and asset versions, download size, free disk, settings — then exit without touching anything.
--version=vX.Y.Z
Install that published release instead of the current one. A version that is not published is a hard error, and nothing on the host is changed.
-y, --yes
Never prompt. Takes the prior answer, or the default, for every question. For unattended runs.
--rollback
Restore the previous images and the newest pre-upgrade snapshot, then wait for the server to become ready again.
--auto-rollback
If the server does not become ready after an upgrade, roll back automatically instead of stopping to ask.
--skip-dns-check
Do not verify that your domains resolve to this host. For split-horizon DNS or a pre-cutover install.
-h, --help
Print every flag, and the environment variables behind them, then exit.

Two rules worth knowing: --dry-run cannot be combined with --rollback, and --auto-rollback does nothing on a first install — there is no previous version to return to yet.

Upgrading

Re-run the same command. The installer finds the existing install and upgrades it in place, offering your previous answers as defaults — press Enter to keep each one. Only artifacts whose published version actually changed are downloaded again.

Preview it first. This changes nothing:

$ curl -fsSL https://web-dev.trixibot.com/install.sh | sudo bash -s -- --dry-run

Carried across untouched

  • Your data volume, and the jwt_secret that decrypts everything in it.
  • The generated admin password.
  • Every key in the existing .env — including HTTP_PORT / HTTPS_PORT and anything you added yourself.

Rewritten every run

  • deploy/docker-compose.yml and the Traefik dynamic config.
  • The settings the installer manages: domains, ACME email, TLS mode, ports.
Before the new version starts, the installer snapshots your database and config to /opt/devgrail/backups/ and keeps the five most recent. It then waits for the server to pass its readiness check, and exits with an error, the logs and the snapshot path if it never does — so a failed migration cannot hide behind a success message.

If an upgrade goes wrong

Every upgrade keeps the version it replaced tagged as :previous, so going back is a tag change and a restart rather than another download:

$ curl -fsSL https://web-dev.trixibot.com/install.sh | sudo bash -s -- --rollback
  • That restores the previous images and the newest pre-upgrade snapshot, then waits for the server to come back.
  • Add --auto-rollback to the upgrade itself to answer that question in advance, for unattended runs.
  • Older images are only cleaned up once the new version is confirmed healthy, so the rollback target is always there when you need it.
  • Restoring images without the snapshot leaves the newer database schema in place, and the older binary will refuse to start against it. That refusal is deliberate.

Good to know

sudo does not pass your environment variables
DEVGRAIL_DOMAIN=example.com curl … | sudo bash silently drops the variable, and you get prompted anyway — or, with --yes, the run stops. Put the assignment after sudo instead, or use sudo -E.
Never edit deploy/docker-compose.yml
It is re-fetched and rewritten on every run, so your edit disappears at the next upgrade without a word. Put local changes in docker-compose.override.yml — the installer never touches that file, and Compose merges it over the base.
Workspace volumes are in no backup
Snapshots carry the database and config.yaml. The contents of /workspace live in per-workspace Docker volumes and are yours to back up — usually they are already backed by a git remote.
The DNS check warns, it does not block
A pre-cutover install, split-horizon DNS and a record that has not propagated yet all look identical from the host, so the installer reports what it found and asks before continuing.
Two runs cannot race
The installer takes a lock for the length of the run. A second one waits for it rather than writing over the first, and says so.

Uninstalling

The default run tears the stack down but keeps your data, so a later install picks up where you left off:

$ curl -fsSL https://web-dev.trixibot.com/uninstall.sh | sudo bash

Removed

  • The Compose stack: server, Traefik and the docker-socket proxy.
  • Every workspace container.
  • The devgrail-server and devgrail-container images.

Kept

  • The database volume and your Let's Encrypt certificates.
  • Every workspace volume.
  • Config in /opt/devgrail and /etc/devgrail — so re-running install.sh resumes in place.

To remove all of it — data volume, certificates, workspace volumes and config:

$ curl -fsSL https://web-dev.trixibot.com/uninstall.sh | sudo bash -s -- --purge
Copy the backup off the host first. A purge takes a final backup into /opt/devgrail/backups/ and then deletes that directory along with everything else, so the archive goes with it. Pass --no-backup to skip the backup entirely — the one way to destroy the data outright. Docker itself is always left installed.

Inspect everything first

Piping a script into a root shell deserves a read-through. Every file the installer uses is served straight from this site: