Server install (docker compose)
The canonical install is a self-contained tarball and a sudo ./install.sh. The tarball ships every docker image (ongrid, ongrid-web, singchia/frontier) so there's no Docker Hub pull — the install works in restrictive networks and air-gapped.
For air-gapped specifics see air-gapped install. For a faster walk-through see Quickstart. For the systemd-native install (no docker), see Linux server platform.
Prereqs
A Linux host with:
- 4 GB RAM minimum, 8 GB recommended (Prometheus + Loki + Tempo + MySQL + manager all live here).
- 20 GB free disk under
/var/lib/ongrid(the data root). Adjust withONGRID_DATA_DIR=…if you want it elsewhere — NFS / iSCSI / NVMe all work; it's a plain bind-mount. - Docker ≥ 24 with the
docker composev2 subcommand (docker compose versionshould print). - openssl — used by
install.shto mint the self-signed TLS cert on first boot iftls.crt/tls.keyaren't already present. - Inbound TCP/443 open from your browser + every edge.
- Inbound TCP/40012 open from every edge (the frontier broker).
- A public IP or domain that all edges can reach. This goes into ONGRID_PUBLIC_URL and is the single most common misconfiguration.
Don't use a private IP for ONGRID_PUBLIC_URL
The installer auto-detects an IP and prompts you to confirm. If you accept a 10.x / 192.168.x / 172.16-31.x address but your edges live on the public internet, logs and traces will silently fail to push. The control-plane tunnel still works (it goes through nginx independently), so the edge looks healthy in the UI — but Loki and Tempo stay empty.
Install
# Pick the latest tag at https://github.com/ongridio/ongrid/releases
VER=v0.7.159
gh release download "$VER" \
--repo ongridio/ongrid \
-p 'ongrid-*-linux-amd64.tar.xz*'
# Verify the sha256 — the .sha256 sidecar ships with each release.
sha256sum -c "ongrid-${VER}-linux-amd64.tar.xz.sha256"
tar xf "ongrid-${VER}-linux-amd64.tar.xz"
cd "ongrid-${VER}-linux-amd64"
sudo ./install.shUseful flags:
| Flag | What |
|---|---|
--mode systemd | Switch to native systemd install (no docker). Dispatches to systemd/install-systemd.sh. See Linux server platform. |
--with-deps | systemd-only. Auto-install mariadb + nginx + grafana via apt/dnf, pull pinned prom / loki / tempo / qdrant binaries with sha256 verify. |
--profile monitoring | compose-only. Expose Prometheus on port 9090 to the host (useful for laptop dev; off by default in prod). |
--no-seed | compose-only. Skip the admin-bootstrap banner output (when re-running an install). |
--force | compose-only. Reinstall on top of an existing install (preserves .env and data volume). |
What install.sh does
In order:
Preflight. Verifies the
dockerCLI is in$PATH, the daemon is reachable, anddocker compose v2is present. Bails early on any of those.Stage configs into
${ONGRID_INSTALL_DIR:-/opt/ongrid}/:docker-compose.yml,.env.example,VERSION,frontier.yaml,prometheus.yml(bind-mounted into the prom container at/etc/prometheus/),prometheus-rules.yml(ADR-026 self-observability alerts),loki-config.yaml,tempo-config.yaml,grafana/provisioning,searxng/settings,edge/(per-arch edge binaries + plugin binaries + upgrade bundle).
Create host data dirs under
${ONGRID_DATA_DIR:-/var/lib/ongrid}/and chown them to the uid each container's image runs as:mysql/→999:999(mysql:8.0)prometheus/→65534:65534(prom runs as nobody)loki/→10001:10001tempo/→10001:10001grafana/→472:472embeddings/→65532:65532(manager nonroot user)
Missing this on first boot is what makes prom/loki/tempo/grafana crash with
permission denied on /<datadir>.Stage the bundled embedding model for offline RAG —
/var/lib/ongrid/embeddings/fast-bge-small-zh-v1.5/. If you've already swapped in a custom model the installer leaves it alone.Refresh nginx config from the tarball and, if no certs are present in
${INSTALL_DIR}/certs/, generate a self-signed cert valid 365 days (CN=ongrid, SAN:DNS:ongrid,DNS:localhost,IP:127.0.0.1).Load docker images from
images/ongrid.tar,images/frontier.tar,images/ongrid-web.tarviadocker load. No registry pull required.Create or reuse
.env. If${INSTALL_DIR}/.envexists, keep it (--forcedoes NOT overwrite operator-edited.env). If not, copy from.env.example. Then fill blanks with strong random values:MYSQL_ROOT_PASSWORD,MYSQL_PASSWORD— 24 chars,ONGRID_JWT_SECRET— 64 chars,ONGRID_ADMIN_PASSWORD— 20 chars (recorded for the final banner),GRAFANA_ADMIN_PASSWORD— 20 chars.
Prompt for
ONGRID_PUBLIC_URLif blank. 30-second countdown, default = best-guess host. Reads/dev/ttyso it survivescurl ... | bash.docker compose up -dwith--env-file .env. No-fargument, sodocker-compose.override.ymlauto-loads if you've added one for tweaks.Poll
https://localhost/healthzfor up to 60 seconds via nginx. The manager's/healthzreturns 200 once MySQL is reachable and the agent kernel is wired.Print the install banner — Web URL, API URL, tunnel endpoint, bootstrap admin email + password (only on first install).
After install
You'll see a banner like:
===============================================================
ongrid installation complete
===============================================================
Install dir: /opt/ongrid
Version: v0.7.159
Web UI: https://203.0.113.10/
API URL: https://203.0.113.10/api/v1
Tunnel endpoint: 203.0.113.10:40012 (for edges)
TLS: self-signed cert in /opt/ongrid/certs/ — browsers will warn
on first visit. Replace tls.crt + tls.key with a real cert and
'docker compose -f /opt/ongrid/docker-compose.yml restart nginx'.
---------------- bootstrap admin ----------------
email: admin@example.com
password: 9Xp4hKqf1bL2zRq3Wn7v
>> Record this password NOW. It will not be shown again.
-------------------------------------------------Record the admin password
The bootstrap password is shown once. It's also stored in /opt/ongrid/.env (mode 0600). If you lose both, reset via the reset-admin-password CLI or by editing the DB directly — see first-boot checklist.
Verify the stack
# All containers should be Up
sudo docker compose -f /opt/ongrid/docker-compose.yml ps
# Manager health
curl -fk https://localhost/healthz
# Tail manager logs
sudo docker compose -f /opt/ongrid/docker-compose.yml logs -f ongrid
# Browse to the UI
open https://<your-host>/ # accept the TLS warningReplacing the TLS cert
The self-signed cert is fine for trials but every browser will warn. For production drop a real cert (Let's Encrypt, your corp CA, or whatever) into /opt/ongrid/certs/:
sudo cp fullchain.pem /opt/ongrid/certs/tls.crt
sudo cp privkey.pem /opt/ongrid/certs/tls.key
sudo chmod 600 /opt/ongrid/certs/tls.key
sudo chmod 644 /opt/ongrid/certs/tls.crt
sudo docker compose -f /opt/ongrid/docker-compose.yml restart nginxinstall.sh and upgrade.sh never overwrite operator-provided certs.
Wildcards work
nginx serves *.crt / *.key from certs/; the only filenames the container looks for are tls.crt and tls.key. Symlinks are fine if your cert manager (certbot, acme.sh, cert-manager) keeps the canonical copy elsewhere.
Where things land
| Path | Owner | Purpose |
|---|---|---|
/opt/ongrid/docker-compose.yml | root | Compose definition. |
/opt/ongrid/.env | root, 0600 | Secrets + tunables. |
/opt/ongrid/certs/ | root, 0700 | tls.crt, tls.key. |
/opt/ongrid/edge/ | root | Edge binaries, upgrade bundle (edge-bundle-<arch>-<ver>.tar.gz), install.sh, apply-pending-upgrade.sh. Served read-only via nginx at /edge/. |
/opt/ongrid/prometheus.yml | root | Prom scrape + remote_write config. |
/opt/ongrid/grafana/ | root | Provisioning yamls (datasources, dashboards). |
/var/lib/ongrid/mysql/ | 999:999 | MySQL data. Back this up. |
/var/lib/ongrid/prometheus/ | 65534:65534 | Prom TSDB. 90d retention, 20GB cap by default. |
/var/lib/ongrid/loki/ | 10001:10001 | Loki chunks + index. |
/var/lib/ongrid/tempo/ | 10001:10001 | Tempo blocks. |
/var/lib/ongrid/qdrant/ | root | Vector store. |
/var/lib/ongrid/grafana/ | 472:472 | Grafana state (dashboards, users, SA token). |
/var/lib/ongrid/embeddings/ | 65532:65532 | Offline BGE model cache. |
/var/log/ongrid/ | mixed | Manager log files. |
What's next
- First-boot checklist — model provider, Prom retention, public URL, timezone, the first incident.
- Edge install — register your first edge.
- Upgrade — the
upgrade.shflow, stage-then-swap,.previousrollback. - Environment variables — every
ONGRID_*env var you can tune.