Docker
Prefer containers? The server ships a compose file. The script installer (Set up your box) is the recommended path because it also handles Tailscale; the container path leaves networking to you.
Compose
Section titled “Compose”The shipped file, verbatim:
services: gray: build: . image: gray-selfhost:latest container_name: gray restart: unless-stopped env_file: .env ports: # Loopback by default — safe. Front it with your own TLS + auth before # exposing it. Only bind 0.0.0.0 if something in front authenticates. - "127.0.0.1:8848:8848" volumes: # Brain working dir (/data/workspace) + connections vault (/data/vault). - gray-data:/data # To let the brain work on data you ALREADY have, mount it read-only and # point GRAY_BRAIN_CWD / GRAY_FILE_ROOT at the mount in .env, e.g.: # - /srv/datasets:/data/workspace:ro
volumes: gray-data:cp .env.example .env # fill it indocker compose up -ddocker compose logs -f grayReaching it
Section titled “Reaching it”The container binds to loopback on purpose — don’t expose 8848 publicly.
Publish it inside your tailnet from the host:
tailscale serve --yes --bg --https=8443 http://127.0.0.1:8848…or front it with your own reverse proxy/tunnel. The app then connects to the tailnet HTTPS address. See Tailscale.
Env and data
Section titled “Env and data”Configuration is the same .env as the script install — see
Configuration reference. The gray-data volume holds
the workspace and the credentials vault; treat it like the vault directory in
Data & backups.
Caveats
Section titled “Caveats”- The box code prints to the container log (the log line says claim code):
docker compose logs -f gray. - The box’s HTTPS origin needs configuring manually in
.env: setGRAY_RP_ID=<hostname>.<tailnet>.ts.netandGRAY_RP_ORIGIN=https://<that hostname>. The script installer sets these for you; the container path doesn’t, and the server warns at startup whileGRAY_RP_IDis stilllocalhost.