Deployment
In one minuteA deployment is one process tree serving the API, the agent endpoint and the documentation for its own revision, bound to all interfaces so that tailnet machines can reach it. Deploy natively, as a container, or under systemd.
Native deployment#
python3 tools/deploy.py up --name dev --port 8600 # make deploy
python3 tools/deploy.py status
python3 tools/deploy.py down --name dev
up performs, in order: refuse a name that is still running and a port that is already in use
(make serve, the systemd unit and other deployments default to 8600 too) → record the
revision (commit, dirty flag) → build the documentation into the deployment directory → start
gyre serve --host 0.0.0.0 → wait for health and verify through /api/v1/info that the answer
comes from the process just started → run the smoke test against the tailnet address (loopback
if the host has none) → write var/deploy/<name>/deployment.json, which records whether the
smoke test passed. No record is written if the service has exited by the end of the smoke test.
Health; deployment identity; the documentation home page, search index and llms.txt; presets;
a structured error; pipeline definitions (at least one runnable, none unloadable); the MCP tool
list; a real solver job with an adjoint gradient, submitted with use_cache: false (an answer
from the result cache fails the check); metrics. It uses only the Python standard library and
can be run from any tailnet machine:
python3 deploy/smoke.py http://HOST:8600
GYRE_API_TOKEN=... python3 deploy/smoke.py http://HOST:8600 # token-protected deployment
A deployment protected by GYRE_API_TOKEN is tested with the same variable set for the script,
or with --token. up passes its environment on to the service (without GYRE_DEVICE, which
the service sets per worker) and to the smoke test, so setting the variable for up covers
both.
The service binds 0.0.0.0, never loopback only. Every URL below works from another
tailnet device with the host’s tailnet name or address:
| URL | |
|---|---|
http://HOST:8600/ |
documentation built from the deployed revision |
http://HOST:8600/api/v1/info |
which revision and deployment is answering |
http://HOST:8600/api/docs |
interactive API explorer |
http://HOST:8600/mcp |
agent endpoint |
Container#
GYRE_COMMIT=$(git rev-parse HEAD) docker compose -f deploy/compose.yaml up -d --build
| Name | Purpose |
|---|---|
GYRE_CONSTRAINTS |
the tested dependency set to install: constraints/tested-cpu.txt (default; JAX 0.11.2, which also runs on CPUs without AVX2) or constraints/tested-cuda12.txt for NVIDIA GPUs |
GYRE_EXTRAS |
extras of the package to install: service,docs (default), service,docs,gpu with the CUDA set |
GYRE_COMMIT |
revision stamped into the image and its documentation |
GYRE_PORT |
published host port (default 8600, bound on all interfaces) |
GYRE_API_TOKEN |
require a bearer token |
GYRE_SERVICE_GPU |
auto, on or off (default off in the container) |
GYRE_PIPELINES_ALLOW_SHELL |
1 lets the service start pipelines with shell stages (see Configuration). The compose file forwards it from the environment docker compose is started in (GYRE_PIPELINES_ALLOW_SHELL=1 docker compose … up); nothing needs to be added to the file |
The image builds the documentation at image-build time from the verification results present
in the build context, so a container and its documentation cannot disagree. Job data and the
JIT cache live on the /data volume.
make image (deploy/image_smoke.sh) builds the image and smoke-tests a throw-away
container of it on port 8602. On a machine whose Docker build sandbox cannot resolve host
names (the package download fails with “Temporary failure in name resolution” although
docker run works), set GYRE_BUILD_NETWORK=host for that script, or pass
--network host to docker build.
The image also ships the analysis pipeline definitions of its revision (pipelines/ without
ci.yaml) and examples/ under /app; the OpenMDAO example needs the mdo extra, which the
image does not install. To serve your own pipeline definitions, bind-mount a directory over
/app/pipelines (deploy/compose.yaml carries a commented example).
systemd#
cp deploy/gyre.service ~/.config/systemd/user/
systemctl --user enable --now gyre
Configuration#
| Variable | Default | Effect |
|---|---|---|
GYRE_DEPLOYMENT |
dev |
name reported by /api/v1/info and shown in the documentation footer |
GYRE_API_TOKEN |
unset | bearer token for /api/v1 and /mcp (/api/v1/health and /api/v1/info stay open) |
GYRE_SERVICE_GPU |
auto |
whether workers may use a GPU |
GYRE_MAX_SESSIONS |
4 | concurrent sessions |
GYRE_SESSION_IDLE_S |
1800 | idle time before a session is closed |
GYRE_PIPELINES_DIR |
pipelines |
directory of the pipeline definitions the service lists, relative to its working directory (/app/pipelines in the image) |
GYRE_MESH_MAX_BYTES |
67108864 (64 MiB) | largest mesh file PUT /api/v1/meshes/{name} accepts (413 MeshTooLarge above it); the arrays of an .npz upload may take up to four times this value uncompressed and must also stay within GYRE_MESH_MAX_EXPANDED_BYTES (422 InvalidMesh). Read at each request. A value that is not a positive integer is replaced by the default |
GYRE_MESH_STORE_MAX_FILES |
256 | number of meshes the mesh store holds (507 MeshStoreFull). A value that is not a positive integer is replaced by the default |
GYRE_MESH_STORE_MAX_BYTES |
4294967296 (4 GiB) | total size of the mesh store in bytes (507 MeshStoreFull). A value that is not a positive integer is replaced by the default |
GYRE_MESH_DIR, GYRE_MESH_RESTRICT |
set by the service | while it runs, the service sets GYRE_MESH_DIR to <data>/meshes (absolute path) and GYRE_MESH_RESTRICT=1 in its own environment, whatever they were before; its job workers, session processes and pipeline runs inherit them and open mesh files only by bare name from that directory. Outside a service, GYRE_MESH_DIR is a directory in which the CLI and Python look for a relative mesh path that does not exist as given |
GYRE_MESH_MAX_EXPANDED_BYTES |
1073741824 (1 GiB) | largest uncompressed size of the arrays of an .npz mesh the reader accepts, in any process, the service included (an upload above it is a 422 InvalidMesh). A value that is not a positive integer is an error: every read of an .npz mesh fails with GYRE_MESH_MAX_EXPANDED_BYTES must be a positive integer, got '…' |
GYRE_PIPELINES_ALLOW_SHELL |
unset | 1 lets the REST API and the MCP tools start pipelines that contain shell stages (for example ci). Without it such definitions are listed with runnable: false and a reason, and starting one returns 403 PipelineNotRunnable; they stay runnable on the host with gyre pipeline run |
GYRE_DEVICE |
auto |
device of a solver process (the service sets it per worker) |
GYRE_JIT_CACHE |
~/.cache/gyre/jit |
persistent compilation cache directory, or off |
GYRE_LOG_LEVEL |
info |
uvicorn log level |
GYRE_SERVICE_URL |
http://127.0.0.1:8600 |
read by the stdio MCP proxy only (python -m gyre.agent): the deployment it forwards to when --service is not given (agent interface) |
gyre serve --workers N sets the number of concurrent job workers (GPU jobs are serialised).
- Identity.
GET /api/v1/inforeturns version, commit, process id, start time and deployment name; the documentation footer shows the same.upaccepts the answer only if it reports the deployment’s name and a start time not earlier than the launch; otherwise it stops the process it started. It passes the name and start time on to the smoke test, which stops at once if another process answers. - Monitoring. Scrape
GET /api/v1/metrics. An unexpected fault is answered 500InternalErrorwith an incident id; the service log (loggergyre.service) has the exception under the same id. - Mesh uploads. Without
GYRE_API_TOKEN, anyone who reaches the service can upload meshes up to the limits above. Uploads are validated before they are stored and are addressed by name only; a request cannot make the service read or write a path outside<data>/meshes. - Data. The data directory (
gyre serve --data, defaultvar/service) holdsjobs/<id>/(request, status, events, artefacts),cache.json,meshes/(the mesh store: one file per uploaded mesh) andpipelines/(runs/<id>/per run andwork/, the stage directories and their cache). Deleting a job directory is safe. Mesh files can be copied intomeshes/or removed by hand; a file the reader rejects is listed with anerror. The result cache is keyed by the case specification, the requested gradients, error estimates and order selection, the contents of a mesh file the case names, and a fingerprint of the code (version, revision and the package sources). An upgrade can therefore keep its data directory: results computed by other code are not reused, and old job records stay readable but are never served as cache hits.<data>/cache.jsonis only an index of that cache; the job records are the source of truth. - Upgrades. Deploy the new revision under a new name and port, smoke-test it, then stop the
old one:
tools/deploy.pykeeps deployments side by side.uprefuses a port that is already in use, so the new revision needs its own--port.