Run Mastra agents and workflows on your own VM
The TypeScript agent framework, with Studio, memory, and a VM per thread.
Shelley handles the setup. About 10 minutes.
Prefer a terminal?
curl -fsSL https://exe-staging.dev/deploy/mastra/prompt.txt | ssh exe.dev new --prompt=/dev/stdinMastra is a TypeScript framework for AI agents and
workflows: agents with tools, memory, and a workspace to run commands in;
workflows of typed steps, which can run on a schedule; and Studio, a web
UI to chat with agents, run workflows, and read traces. mastra build
turns a project into one Node.js server that serves its HTTP API and
Studio.
An exe.dev VM runs that server around the clock under systemd, private behind exe.dev login, with its memory and schedules in a SQLite file on the VM's disk. Models come from exe.dev's LLM integration, so no API key is on the VM. And an agent can get a VM of its own for each conversation, made through exe.dev's API, to install, build, and serve whatever it likes.
What you get#
- Node.js 24 and a Mastra project from create-mastra in ~/my-agents, a git repository
- mastra.service enabled, serving the API and Studio on port 8000, private to the VM's owner, and back after a reboot
- Model calls through exe.dev's LLM integration, with no OpenAI API key on the VM
- Threads, memory, workflow runs, and schedules in ~/my-agents/mastra.db
- The hn-digest workflow, run once and scheduled for 07:00 UTC every day
What's running, and where
https://my-mastra.exe.xyz/
Studio, to chat with agents, run workflows, and read traces
https://my-mastra.exe.xyz/api/
Mastra's HTTP API, behind the same exe.dev login
ssh my-mastra.exe.xyz
the project, its data, and logs
Deploy with Shelley#
Click Deploy and Shelley will set up Mastra on a new VM, then check that it works. No config-file wrangling required.
Prefer a terminal?
curl -fsSL \
https://exe-staging.dev/deploy/mastra/prompt.txt \
| ssh exe.dev new --prompt=/dev/stdinWhat Shelley is told
Set up Mastra on this VM by following the exe.dev recipe at https://exe-staging.dev/deploy/mastra.md from top to bottom.
The recipe's example VM name is my-mastra. This VM's name is the output of `hostname`: use it wherever the recipe writes my-mastra. Skip the step that creates the VM; you are on it.
You are done when all of these are true. Check each one yourself:
- Node.js 24 and a Mastra project from create-mastra in ~/my-agents, a git repository
- mastra.service enabled, serving the API and Studio on port 8000, private to the VM's owner, and back after a reboot
- Model calls through exe.dev's LLM integration, with no OpenAI API key on the VM
- Threads, memory, workflow runs, and schedules in ~/my-agents/mastra.db
- The hn-digest workflow, run once and scheduled for 07:00 UTC every day
Run "Set up Mastra by hand" from "On the VM", skipping VM creation and
SSH, then "Add a scheduled workflow". Wait for /health before each
curl to the server. Skip "Give each conversation a VM" unless the owner
asks: only they can create its integration, from their own computer;
never ask for an exe.dev or OpenAI API key instead. Keep port 8000
private: never run share set-public. Finish with https://<vm>.exe.xyz/,
using this VM's name.
Finish with the URLs I should open.
Also at /deploy/mastra/prompt.txt. The recipe it follows is /deploy/mastra.md.
Set up Mastra#
Deploy sets it up on a new VM. To do it yourself:
Set up Mastra by hand
On your computer:
ssh exe.dev new --name=my-mastra
ssh my-mastra.exe.xyz
On the VM, install Node.js 24 and make a project with Mastra's starter agent (about a minute):
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt-get install -y nodejs
npx -y create-mastra@1 my-agents --llm openai < /dev/null
Point OpenAI models at the LLM integration, and build the server with Studio:
cd ~/my-agents
(umask 077; printf 'OPENAI_BASE_URL=https://llm.int.exe.xyz/v1\nOPENAI_API_KEY=exe.dev\n' > .env)
npm run build -- --studio
Run it under systemd on port 8000, the VM's HTTPS address.
MASTRA_AUTO_DETECT_URL makes Studio call the API at the address it was
opened from, rather than localhost:
sudo tee /etc/systemd/system/mastra.service >/dev/null <<'EOF'
[Unit]
Description=Mastra server
After=network-online.target
Wants=network-online.target
[Service]
User=exedev
WorkingDirectory=/home/exedev/my-agents
EnvironmentFile=/home/exedev/my-agents/.env
Environment=NODE_ENV=production PORT=8000 MASTRA_STUDIO_PATH=.mastra/output/studio MASTRA_AUTO_DETECT_URL=true
ExecStart=/usr/bin/node .mastra/output/index.mjs
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable --now mastra
Check it after a few seconds with curl -s localhost:8000/health. Expect
{"success":true}.
Open Studio#
Open https://my-mastra.exe.xyz/ and sign in to exe.dev. Under
Agents, the starter agent chats with GPT through the LLM integration,
searches and fetches the web, keeps memory across threads, and reads,
writes, and runs commands in ~/my-agents/workspace on this VM; it asks
before deleting anything. Workflows, Traces, and Logs are in
the sidebar.
The project is yours to change: agents in src/mastra/agents, tools in
src/mastra/tools, and everything registered in src/mastra/index.ts.
Shelley, or any coding agent, can make the changes; the project's
AGENTS.md tells it how. After a change, on the VM:
cd ~/my-agents
npm run build -- --studio && sudo systemctl restart mastra
Add a scheduled workflow#
hn-digest.ts is a workflow
of two steps: fetch the top Hacker News stories, then have a model write
a short digest to workspace/digests/<date>.md, where the starter agent
can read it. It runs every day at 07:00 UTC. On the VM, add it and
register it:
cd ~/my-agents
curl -fsSL --create-dirs -o src/mastra/workflows/hn-digest.ts https://exe-staging.dev/deploy/mastra/hn-digest.ts
sed -i "s|^import { agent } from './agents/agent';|&\nimport { hnDigest } from './workflows/hn-digest';|; s|^ agents: { agent },|&\n workflows: { hnDigest },|" src/mastra/index.ts
grep -c hnDigest src/mastra/index.ts
npm run build -- --studio && sudo systemctl restart mastra
Expect 2 from grep. Once /health answers, run it now rather than
at 07:00:
curl -s -X POST localhost:8000/api/workflows/hn-digest/start-async \
-H 'Content-Type: application/json' -d '{"inputData":{"count":5}}'
Expect "status":"success" and the digest, after about ten seconds.
Then ask the starter agent in Studio for today's digest. Studio lists the
schedule at https://my-mastra.exe.xyz/workflows/schedules, with
Pause and Resume; it's kept in mastra.db, so it survives
restarts.
The HTTP API answers the same way your own code calls it. From this VM:
curl -s localhost:8000/api/agents/agent/generate -H 'Content-Type: application/json' \
-d '{"messages":[{"role":"user","content":"Hi"}],"memory":{"thread":"t1","resource":"me"}}'
From another of your exe.dev VMs, use a
VM-to-VM integration to my-mastra,
which keeps port 8000 private.
Give each conversation a VM#
The starter agent runs commands on the Mastra VM itself. For agents that install packages, run Docker, or serve apps, give each thread a VM of its own instead. exe-sandbox.ts is a Mastra workspace sandbox that is an exe.dev VM, made with exe.dev's TypeScript SDK on the thread's first command and kept until you delete it. vm-agent.ts is an agent that uses one per thread.
The Mastra VM makes those VMs through an integration that holds the API key: no key is ever on a VM. On your computer:
ssh exe.dev integrations add exe-dev --name mastra-vms \
--tag mastra-sandbox --cmds new,ls,ssh,rm --attach vm:my-mastra
The integration can make, reach, and delete VMs tagged mastra-sandbox,
and nothing else; the VMs it makes get that tag. Only the Mastra VM can
use it. On the VM, check it, then add the two files and register the
agent:
curl -s -X POST https://mastra-vms.int.exe.xyz/exec -d ls
cd ~/my-agents
npm install @exedev/sdk@0.4
curl -fsSL -o src/mastra/exe-sandbox.ts https://exe-staging.dev/deploy/mastra/exe-sandbox.ts
curl -fsSL -o src/mastra/agents/vm-agent.ts https://exe-staging.dev/deploy/mastra/vm-agent.ts
sed -i "s|^import { agent } from './agents/agent';|&\nimport { vmAgent } from './agents/vm-agent';|; s|^ agents: { agent },| agents: { agent, vmAgent },|" src/mastra/index.ts
grep -c vmAgent src/mastra/index.ts
npm run build -- --studio && sudo systemctl restart mastra
Expect {"vms":[]} from the check and 2 from grep. In Studio, open
VM Agent and ask: "Make a small Flask to-do app, serve it on port
8080, and give me the link." The first command takes a few seconds while
the VM starts. The link, https://mastra-<id>.exe.xyz:8080/, opens for
you after exe.dev login. A later message in the same thread finds the
same VM, with its files and running services; a new thread gets a new
one.
Warning
VM Agent runs commands with sudo, without asking. That's what the thread VMs are for: they hold nothing else. Keep the Mastra VM, which can make VMs on your account, to yourself, and never attach the integration to the
mastra-sandboxtag, which would let thread VMs do the same.
To use the sandbox in your own agents, pass a resolver as vm-agent.ts
does, or a fixed one: sandbox: new ExeSandbox({ id: 'builds' }).
ExeSandbox also takes workingDirectory, timeout, and env. Off
exe.dev, pass token: an exe.dev API key from
ssh exe.dev ssh-key generate-api-key --label=mastra --tag=mastra-sandbox --cmds=new,ls,ssh,rm --exp=30d.
Clean up#
Thread VMs stay until you delete them; each counts toward your plan's VM
limit. On your computer, ssh exe.dev ls lists them, with their thread
as the comment. To delete every VM tagged mastra-sandbox:
ssh exe.dev ls --json |
jq -r '.vms[] | select(.tags // [] | index("mastra-sandbox")) | .vm_name' |
xargs -r ssh exe.dev rm
When you're done with them,
ssh exe.dev integrations remove mastra-vms. ssh exe.dev rm my-mastra
deletes the Mastra VM and its data.
Troubleshooting#
- Studio loads but can't reach the server, or asks for an instance URL:
check that the unit has
MASTRA_AUTO_DETECT_URL=true, thensudo systemctl daemon-reload && sudo systemctl restart mastra. sudo journalctl -u mastra -fshows the server's log; build errors appear innpm run build, not there.Connection erroror401from the model: check.env. The LLM integration is reachable only from exe.dev VMs.- VM Agent fails with
403andintegration not found: run the command in Give each conversation a VM with this VM's name, orssh exe.dev integrations attach mastra-vms vm:<this-vm>. - VM Agent fails during
new: exe.dev refused a VM, often because the plan's VM limit is reached. Delete old thread VMs.
Frequently asked questions#
Do I need an OpenAI API key?
No. Mastra's model router sends openai/... models to OPENAI_BASE_URL, which .env points at exe.dev's LLM integration; it bills your exe.dev account, and the key in .env is a placeholder. For other providers, put their key in .env and use their model ids, such as anthropic/....
Who can open Studio?
Only you, after exe.dev login: port 8000 is private through the HTTPS proxy. Studio has full access to your agents, workflows, and tools, so share it only with people you'd give a shell on the VM: ssh exe.dev share add my-mastra ann@example.com. Mastra's own Studio auth isn't needed.
Where is my data, and how do I back it up?
On the VM's disk, in ~/my-agents: mastra.db (SQLite: threads, messages, memory, workflow runs, and schedules), mastra.duckdb (traces), and workspace/ (the starter agent's files). sqlite3 mastra.db ".backup mastra-backup.db" copies the database while the server runs. For Postgres instead, see PostgreSQL and Mastra's @mastra/pg.
How is this different from the Mastra platform, Vercel, or Railway?
It's one always-on machine, not a function: schedules, background tasks, and the SQLite file all live on it, and nothing needs a separate database or cron service. It's private until you share it. There's no autoscaling; a bigger VM (ssh exe.dev resize) is the way up.
How does the VM-per-thread sandbox compare with E2B or Daytona?
It plugs into the same place, a Workspace sandbox resolved per thread. Each sandbox is a whole Ubuntu VM with sudo, Docker, and systemd, so an agent can install anything and serve web apps at a private HTTPS address, and the VM is still there days later. Thread VMs share your plan's CPU and memory, but each counts toward its VM limit. exe-sandbox.ts doesn't do mounts or Mastra's background-process tools; agents run servers under systemd instead.
Does Mastra send telemetry?
The CLI and Studio collect anonymous usage analytics. Add MASTRA_TELEMETRY_DISABLED=1 to .env and rebuild to turn them off. The starter's Mastra platform exporter sends traces only once you configure a platform access token.
Updated Oct 31, 2026 · tested end to end Oct 31, 2026 · Markdown · manifest.json · Something wrong? Tell us