Your Dockerfile and files upload straight to the build, so a private codebase never needs a registry. A
FROM on a private registry and a RUN step that needs a token both work through build secrets.
A complete example lives in agent37-platform/custom-agent-image: a working agent app (auth, chat, files, integrations) whose agents run an image it builds itself from one Dockerfile and one skill folder. Click Use this template, edit the Dockerfile, run npm run release:agent. Smaller ready-to-build recipes live in the Agent37 Cookbook, like hermes-vnc-desktop, a live desktop view of the agent’s browser.
This page is the walkthrough; Templates → build on the Hermes base image is the reference for the contract.
When you register a template, Agent37 copies your image once into private storage and pins it by digest. That snapshot is what every instance runs, so registering takes up to a few minutes for a large image. Re-pushing the same tag later does nothing: publish a new tag and update the template, or re-run the cloud build. Your image is stored privately and never republished.
Paste this into your coding agent
1. Choose a starting image
To customize Hermes, build onghcr.io/agent37-platform/hermes-base. It includes Hermes, the gateway, Chromium, and the standard toolchain. This example adds a CLI:
/usr/local/bin and everything else into /usr/local or /opt, never /home/node or /home/linuxbrew, which are masked at runtime. Keep the base ENTRYPOINT. See the full contract.
Skills don’t belong in the image. Hermes reads them from An app that provisions agents does this once, right after create. The skill then persists across restarts and updates.
~/.hermes/skills, which lives on the instance’s persistent volume, so anything the image writes there is masked. Install them into a running instance instead, over exec or the files API:curl
0.0.0.0 and note its listening port; you will pass that as default_port when you register the template. An image with no HTTP service can omit default_port and be driven through exec, but it still needs a long-running ENTRYPOINT or CMD.
2. Verify it locally (optional)
You don’t need Docker to publish; the cloud build does the real build. If you have Docker, a local build is still the fastest way to test before publishing. Plaindocker build stores the amd64 result in your local Docker, including on an Apple Silicon Mac:
linux/amd64 build is also the artifact you push.
3. Register the image
You have two ways to get the image to Agent37. Either way the resulting image is capped at 8 GB decimal (8,000,000,000 bytes), and either way the platform copies it once into private storage at registration.
Registration is synchronous and can take a few minutes for a large image. The returned
image_digest identifies the immutable private copy every instance runs. Re-pushing a mutable source tag does not change that copy.Build it in the cloud
Use this path when the image isn’t in a registry yet, or you would rather not run one. You upload a small build context (theDockerfile and the files it COPYs), and Agent37 builds the image on its own infrastructure:
- Packs the directory (default
.; it must haveDockerfileat its root) into a gzipped context, excluding.gitand your.dockerignorepatterns (plain patterns only;!negations are ignored). The context is capped at 1 GB; it holds the Dockerfile’s inputs, not the image, so it stays small. - Everything else in the folder ships with the context, a stray
.envor key file included, and aCOPY . .bakes it into the image. Check the folder, or add a.dockerignore, before you build. - Builds the image on Agent37’s infrastructure and streams the live build log to your terminal. On failure the command exits non-zero with the failing step visible.
- Publishes the result as the workspace template.
--namedefaults to the folder name;--default-port <port>sets the template’s default port. Re-building an existing name publishes a new template revision; existing instances never change. - Ctrl-C does not cancel the build; it continues server-side and still publishes on success.
image_digest but no image_ref, because there is no public registry reference.
To script the same flow without the CLI, see the raw contract in
Templates → build an image in the cloud.
Build secrets
ARUN step that needs a credential, cloning a private repository or installing from a private package index, takes it as a build secret. The value is available at /run/secrets/<id> for that step only, is never written into the image, and is not stored by Agent37.
--secret id=NAME reads the value from the environment variable NAME; id=NAME,env=VAR reads a different variable and id=NAME,src=PATH reads a file, the same grammar as docker build --secret. Pass the flag once per secret.
A FROM on a private registry takes the login the same way, and it is not stored either:
Import a registry image
Push the image to any OCI registry, then register its fully qualified reference. For example:registry_auth (a personal access token with read:packages as the password). In CI, GitHub’s ubuntu-latest runner is already linux/amd64, and the built-in GITHUB_TOKEN can push to ghcr.io/<owner>/<repo> with permissions: packages: write. Tag the image with the commit sha and register that, never latest.
Register a specific tag, not latest:
curl
curl
registry_auth again if the registry is private.
4. Create an instance
Register once, then create instances from the template name:curl
curl
If the instance comes up
failed instead of running, read its logs. An entrypoint that exits, a missing binary, or a service that never listens on default_port are the usual causes. exec cannot help until the container is running.5. Give it a model
hermes-base is clean: it boots with no LLM provider wired. That does not mean the instance has
no model available. Every instance, including one running an image you built from scratch, gets
a working OpenAI-compatible endpoint and a Composio MCP server in its environment, as
AGENT37_MANAGED_TOKEN, AGENT37_LLM_PROXY_URL, and AGENT37_COMPOSIO_MCP_URL. Pointing your
agent at those is the
shortest path to a running agent, costs no setup, and meters to the instance
budget. Managed services in your image is
the reference, and pi-agent-image is a
complete image that does it in two config files with no credentials in either.
Bring your own model instead
To run an instance on your own model, point Hermes at any OpenAI-compatible endpoint, your own proxy or a provider directly, by writing~/.hermes/config.yaml on the instance:
GET /v1/models (to resolve the model id) and POST /v1/chat/completions (the turn). Anything that speaks them works: a provider directly, or a small proxy of your own that forwards to one with your key.
Write the config over exec or the instance terminal; it lives on the persistent volume, so it survives restarts. Then send a message and the agent runs on your model.
Want this as a finished app rather than a config file? Use your own model is a forkable kit that does exactly this per agent: your key behind a proxy, a revocable token per instance, and per-agent spend caps.
Keep it current
Your image freezes its base; rebuilding is how it picks up platform updates. Each template revision is an immutable snapshot, so a new build takes effect only after the template changes: re-run the cloud build under the same name, or publish a new registry tag and PATCHimage_ref. Every successful build and every changed image_ref increments the template’s automatic revision; a re-build counts even when its digest matches the previous image. PATCHing the same image_ref string deliberately reuses the existing snapshot and revision.
Existing instances keep their installed template_revision. Update each instance to recreate it from the template’s current revision. Anything on the persistent volume (~/.hermes/skills, ~/.hermes/config.yaml, the agent’s files) survives that update untouched, so a changed skill needs the same exec write again, not a rebuild.