How to deploy a capsule to the cloud
Beta feature
mur deploy is a beta feature and is hidden by default. Enable it first with
mur beta enable deploy. The same flag also enables its teardown counterpart,
mur destroy (used below) — both are gated together. Behavior and flags may
change in future releases.
mur deploy uploads your capsule files to an existing VM via SSH, starts the capsule, and returns a public A2A endpoint. You create and manage the VM through your cloud provider's dashboard — mur deploy never provisions or terminates VMs on your behalf.
The relevant manifest options are:
| Option | Controls |
|---|---|
| mur_version | Pins the exact mur binary version installed on the VM |
| inference.driver.artifact | Inference driver — required for agent capsules |
| inference.system_prompt_file | Local file uploaded alongside the manifest as the system prompt |
| lifecycle.task_acceptance | Whether the deployed capsule accepts incoming A2A tasks |
| lifecycle.after_task | What the capsule does after a task completes |
Prerequisites
- A working agent capsule that runs locally with
mur run(aninferenceblock is required) - A VM you have created through your cloud provider, with SSH access and a public IP address
Step 1 — create a VM
Log in to your cloud provider's control panel and create a new Linux VM. Supported providers include Hostinger, Hetzner, DigitalOcean, AWS, and any other provider that gives you SSH access and a public IP.
Choose Ubuntu 22.04 LTS or 24.04 LTS as the operating system. All major providers offer it. Other distributions (Debian, Rocky Linux, AlmaLinux, Fedora Cloud) also work. Alpine Linux is only compatible if you build mur with the x86_64-unknown-linux-musl target.
Make a note of the VM's public IP address before continuing.
Step 2 — pin the mur version (recommended)
Add a mur_version field to your murmur.yaml:
name: my-agent
version: "0.1.0"
mur_version: "X.Y.Z"
artifacts:
- name: murmur-driver-anthropic
version: "1.0.0"
runtime: driver
capabilities:
network:
allow:
- https://api.anthropic.com
inference:
transport: http
endpoint: https://api.anthropic.com
model: claude-sonnet-5
api_key: ${ANTHROPIC_API_KEY}
driver:
artifact: murmur-driver-anthropic
When mur_version is set, mur deploy downloads that exact binary from GitHub releases and caches it at ~/.murmur/bin/mur-{version}-linux-x86_64. Subsequent deploys with the same version skip the download entirely.
If mur_version is omitted, the version of the mur binary currently running on your machine is used instead.
Step 3 — deploy
mur deploy \
--host 1.2.3.4 \
--manifest ./murmur.yaml \
--env ANTHROPIC_API_KEY=sk-ant-...
mur deploy shows every step upfront as pending · before any work starts.
Steps use a two-level hierarchy: the group header at level 1, individual items indented at level 2.
When a group finishes, the level-2 items collapse — only the checkmarked summary stays.
Initial state (everything pending):
· ↓ 3 artifacts 2↓ 1 cached
· ↓ murmur-driver-anthropic@0.4.1
· ↓ murmur-hook-compact@0.3.1
· ↓ murmur-tool-editor@0.4.4
· mur vX.Y.Z
· SSH
· ↑ mur binary
· ↑ files
· ↑ 3 artifacts
· ↑ murmur-driver-anthropic@0.4.1
· ↑ murmur-hook-compact@0.3.1
· ↑ murmur-tool-editor@0.4.4
· start capsule
During downloads (parallel, bytes / total bar per artifact being fetched):
⠸ ↓ 3 artifacts
✓ ↓ murmur-driver-anthropic@0.4.1 cached
⠸ ↓ murmur-hook-compact@0.3.1 2/2 MB [████████████]
⠸ ↓ murmur-tool-editor@0.4.4 7/14 MB [██████░░░░░░]
· mur vX.Y.Z
...
After each group completes, level-2 items collapse. SSH disappears when the first upload starts:
✓ ↓ 3 artifacts 16 MB 1 cached
✓ ↓ mur vX.Y.Z cached
✓ ↑ mur binary 59 MB
✓ ↑ murmur.yaml · instructions.md
✓ ↑ 3 artifacts 21 MB
✓ → capsule started · port 9000
┌──────────────────────────────────────────┐
│ ∞ my-agent │
│ │
│ url http://1.2.3.4:9000 │
│ job job_01954a3b │
│ time 47s │
└──────────────────────────────────────────┘
All output goes to stderr. Save the job_id from the box — you will need it for mur ps and mur destroy.
All flags
| Flag | Default | Description |
|---|---|---|
--host |
(required) | VM public IP address or hostname |
--manifest |
./murmur.yaml |
Path to the capsule manifest |
--ssh-user |
root |
SSH username on the VM |
--ssh-key |
(SSH agent) | Path to SSH private key; falls back to SSH agent / ~/.ssh/id_* |
--mur-binary |
(auto-download) | Path to a pre-built Linux x86_64 mur binary; skips the GitHub download |
--env |
(none) | KEY=VALUE environment variable for mur run on the VM; repeat for multiple |
--env-file |
(auto-detect) | Path to a .env file; KEY=VALUE or export KEY=VALUE; # comments ignored. If neither --env nor --env-file is passed, .env next to the manifest is loaded automatically |
--workdir |
(none) | Local directory to upload as the capsule working directory |
--deploy-platform |
linux-x86_64 |
Target platform for artifact resolution and staging |
What mur deploy does
- Parses the manifest and resolves every declared artifact for the target platform — fails immediately if any artifact is unavailable, before touching the network
- Checks that every file referenced by the manifest (e.g.
inference.system_prompt_file) exists locally — fails immediately if any file is missing - Validates every
--enventry; a missing=or empty key causes an immediate exit - Resolves the
murbinary: usesmanifest.mur_versionif set, otherwise the running version; downloads and caches if not already cached - Waits up to 30 seconds for SSH to become available on the VM
- Uploads the
murbinary to/usr/local/bin/mur - Uploads the manifest, any manifest-referenced local files, and the optional workdir
- Uploads all pre-staged artifacts to
/root/.murmur/artifacts/ - Writes env vars to
/root/mur-{short_id}/.envwith mode600; env vars come from--env,--env-file, or.envnext to the manifest (auto-detected); skipped when none are set - Sources
/root/mur-{short_id}/.env(if present) and startsmur run --manifest <path> --json; waits up to 120 seconds for startup - Appends the deployment record to
~/.murmur/deployments.json
Steps 1–4 all run locally before any SSH connection. A bad manifest, missing artifact, missing file, or malformed --env entry causes mur deploy to exit with a clear error message and no VM interaction.
Step 4 — verify the agent card
Once mur deploy returns, the capsule's A2A endpoint is publicly reachable. Confirm it is alive by fetching the agent card:
curl http://1.2.3.4:9000/.well-known/agent-card.json
A 200 response confirms the capsule is running:
{
"name": "my-agent",
"version": "0.1.0",
"url": "1.2.3.4:9000",
"capabilities": {
"tools": ["bash"],
"shell": true,
"network": false,
"streaming": true
}
}
Step 5 — send a task
The deployed capsule accepts standard A2A messages at its public URL. Send a task the same way you would to a local capsule:
curl -s -X POST http://1.2.3.4:9000 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "message/send",
"params": {
"message": {
"messageId": "msg-001",
"role": "user",
"parts": [{"text": "Hello from the cloud."}]
}
}
}'
See Connect two capsules with A2A messaging for the full message protocol.
For a capsule to accept incoming tasks it must have lifecycle.task_acceptance: single or queue in its manifest. Without a lifecycle block the capsule accepts no incoming tasks after its initial run.
Step 6 — list deployments
mur ps
JOB_ID PROVIDER IP STATUS URL
----------------------------------------------------------------------------------------------------
job_01954a3b5c7d8e9f0a1b2c3d4e5f6a7b8c manual 1.2.3.4 running http://1.2.3.4:9000
All active deployments are stored in ~/.murmur/deployments.json. Each row maps to one entry.
Step 7 — remove from the deployment list
When you no longer need a deployment, remove it from the tracking list:
mur destroy job_01954a3b5c7d8e9f0a1b2c3d4e5f6a7b8c
This removes the entry from ~/.murmur/deployments.json. It does not stop or delete the VM — shut down the VM from your cloud provider's dashboard separately.
Reference: using a system prompt file
If your capsule reads its system prompt from a file rather than inline YAML, set inference.system_prompt_file in your manifest:
inference:
transport: http
endpoint: https://api.anthropic.com
model: claude-sonnet-5
api_key: ${ANTHROPIC_API_KEY}
system_prompt_file: instructions.md
driver:
artifact: murmur-driver-anthropic
mur deploy automatically detects instructions.md as a referenced local file, verifies it exists before attempting any SSH connection, and uploads it alongside the manifest to the remote deploy directory. No additional flags required.
Reference: passing environment variables
Use --env KEY=VALUE to inject runtime secrets — API keys, tokens, database URLs — into the capsule's environment on the VM. Pass one flag per variable:
mur deploy \
--host 1.2.3.4 \
--manifest ./murmur.yaml \
--env ANTHROPIC_API_KEY=sk-ant-... \
--env DATABASE_URL=postgres://user:pass@host/db
Or point to a .env file with --env-file:
mur deploy --host 1.2.3.4 --env-file ./secrets.env
If neither --env nor --env-file is provided, mur deploy looks for a .env file in the same directory as the manifest and loads it automatically when found. This means running mur deploy --host 1.2.3.4 from a project folder that has a .env file works with no extra flags.
Both KEY=VALUE and export KEY=VALUE formats are accepted; the export prefix is stripped automatically.
In the manifest, reference variables with ${VAR_NAME}:
inference:
transport: http
endpoint: https://api.anthropic.com
model: claude-sonnet-5
api_key: ${ANTHROPIC_API_KEY}
driver:
artifact: murmur-driver-anthropic
When the capsule starts, mur run reads ANTHROPIC_API_KEY from its environment and substitutes it wherever ${ANTHROPIC_API_KEY} appears in the manifest. If the variable is not set, mur run exits with E-MAN-003.
mur deploy writes all --env values to /root/mur-{short_id}/.env on the VM with mode 600 and sources that file before starting mur run. Values are transferred using base64 encoding, so values that contain spaces, equals signs, newlines, or shell metacharacters are preserved exactly. When no --env flags are passed, no .env file is written.
Every --env entry is validated before any SSH connection is made. An entry with no = or an empty key causes mur deploy to exit immediately with an error.
Reference: using a pre-built binary
If you have already built a Linux mur binary — for example, on a CI server — pass it directly instead of letting mur deploy download one:
mur deploy \
--host 1.2.3.4 \
--manifest ./murmur.yaml \
--mur-binary ./target/x86_64-unknown-linux-musl/release/mur
To build the musl binary locally:
rustup target add x86_64-unknown-linux-musl
CARGO_PROFILE_DEV_DEBUG=0 cargo build --release \
--target x86_64-unknown-linux-musl \
-p murmur-cli
musl vs glibc
x86_64-unknown-linux-musl produces a fully static binary that runs on every Linux distribution including Alpine. x86_64-unknown-linux-gnu (glibc) works on Ubuntu, Debian, Rocky, and AlmaLinux, but fails on Alpine.
Summary
| Feature | Detail |
|---|---|
| Command | mur deploy --host <IP> --manifest <path> |
| Progress | All steps shown upfront as pending; each activates in turn; artifact downloads show bytes/total bar; artifact uploads run in parallel (stderr) |
| Output | Success summary box on stderr showing url, job id (job_ prefix + 8 hex chars), and elapsed time |
| VM management | You create and delete VMs through your cloud provider; mur deploy never provisions or terminates VMs |
| Recommended OS | Ubuntu 22.04 LTS or 24.04 LTS |
| mur binary | Auto-downloaded from GitHub releases; pin with mur_version in the manifest |
| Manifest files | inference.system_prompt_file and any other referenced local files are uploaded automatically |
| Env vars | --env KEY=VALUE (repeatable) · --env-file <path> · auto-loads .env next to the manifest if neither flag is passed |
| Artifact pre-staging | All manifest artifacts are resolved and uploaded before the capsule starts |
| Timeout | 30s SSH wait + 120s capsule startup wait |
| State file | ~/.murmur/deployments.json — appended on deploy, entry removed on mur destroy |
| Health check | GET {url}/.well-known/agent-card.json returns 200 when the capsule is alive |
| Tear down | mur destroy <job_id> removes the tracking entry; delete the VM from your provider dashboard |
| List | mur ps — reads ~/.murmur/deployments.json |