Skip to content

Run Mastra agents and workflows on your own VM

The TypeScript agent framework, with Studio, memory, and a VM per thread.

Deploy on exe.dev

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/stdin

Mastra 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
Private https://my-mastra.exe.xyz/ Studio, to chat with agents, run workflows, and read traces
Private https://my-mastra.exe.xyz/api/ Mastra's HTTP API, behind the same exe.dev login
Private ssh my-mastra.exe.xyz the project, its data, and logs
my-mastra.exe.xyz exe.dev VM
Your project The create-mastra 1.37 starter, a git repository you edit ~/my-agents/src
Mastra server @mastra/core 1.81 on Node.js 24, with Studio, the mastra systemd service :8000
Storage Threads, memory, workflow runs, and schedules in SQLite (LibSQL); traces in DuckDB ~/my-agents/mastra.db
GPT models OpenAI, through exe.dev's LLM integration
Thread VMs (optional) One exe.dev VM per conversation, through the mastra-vms integration

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/stdin
What 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-sandbox tag, 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, then sudo systemctl daemon-reload && sudo systemctl restart mastra.
  • sudo journalctl -u mastra -f shows the server's log; build errors appear in npm run build, not there.
  • Connection error or 401 from the model: check .env. The LLM integration is reachable only from exe.dev VMs.
  • VM Agent fails with 403 and integration not found: run the command in Give each conversation a VM with this VM's name, or ssh 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