This walkthrough takes you from zero to a fully deployed Simple Agent Manager
on your own Cloudflare account. We'll generate your config and secrets right here
in your browser — nothing is ever sent anywhere.
Cloudflare account
Workers Paid plan ($5/mo) for Durable Objects and Cloudflare Containers, with R2 and Analytics Engine enabled.
GitHub account
Free tier. Used for authentication and the GitHub Actions deploy pipeline.
A domain on Cloudflare
Any registrar. Use a top-level domain (e.g. example.com), not a subdomain.
You don't need a cloud provider account to start. SAM is bring-your-own-cloud —
you (and your users) add Hetzner, Scaleway, or GCP credentials later through the Settings UI.
Step 1
Your domain and Cloudflare account
Enter the domain you'll use for SAM. This becomes your BASE_DOMAIN and powers
every URL and secret we generate from here on. We also derive a short resource prefix
from this domain so your Cloudflare resources do not collide with other SAM installs.
Your Cloudflare account ID lets us send you to the right account-scoped token pages.
Enter a valid top-level domain, e.g. example.com.
Enter the 32-character Cloudflare account ID.In the Cloudflare dashboard URL, this is the 32-character value after dash.cloudflare.com/.
Use a top-level domain. Cloudflare's free Universal SSL covers *.example.com
but not nested wildcards like *.sam.example.com. The root domain itself isn't used —
only api., app., and *. subdomains are created.
We'll use these URLs
App
API
Workspaces
Resource prefix
Why a top-level domain?
SAM hands every part of your deployment its own hostname — app. for the
dashboard, api. for the control plane, and a throwaway ws-…
subdomain for each running agent. Cloudflare's free wildcard certificate covers
*.example.com, so a top-level domain gets all of them secured automatically.
Point SAM at a nested subdomain like sam.example.com instead and that
wildcard no longer applies — every workspace you spin up would come up without HTTPS.
Step 2
Fork the repository
SAM deploys from your own fork. You'll run the GitHub Actions deploy workflow there once
the production environment is configured.
Keep the fork under the account or organization where you want the deploy to run. You'll
configure its production environment in Step 6.
Why fork instead of using a hosted service?
There's no hosted SAM to sign up for — your fork is your deployment.
Pushing to main is what triggers the GitHub Actions pipeline that
provisions and ships everything onto your own Cloudflare account. You own the code,
the infrastructure, and the data end to end.
Forking (rather than cloning) keeps the link to upstream, so you can pull in new SAM
releases on your own schedule instead of being upgraded out from under you.
Step 3
Create your Cloudflare API token
In the Cloudflare account from Step 1, enable R2 first if it is not already active.
Then create a single API token with exactly the permissions below. Because the token
includes R2 Storage, Cloudflare's final creation screen shows both the
API token and the R2 S3 keys (Access Key ID + Secret) together — copy all of
them before leaving the page. You do not need a separate R2 token.
Set Zone Resources to your domain and Account Resources to your account.
Also enable Analytics Engine (free): Workers & Pages → Analytics Engine → Enable.
The default deploy configures AI Gateway and writes Analytics Engine data, so keep those
enabled even if every user brings their own model keys.
Paste all the values from that one creation screen so we can assemble your final secrets
in Step 6. The R2 S3 keys are the Access Key ID and
Secret Access Key shown alongside the API token; Pulumi uses them for its
state backend.
Open your domain's Cloudflare overview page and copy the Zone ID from the right sidebar.
Open domain overview
Token and secret fields are never persisted to your browser storage — they live only in this tab until you close it.
What does each permission actually do?
This one token lets your deploy pipeline build the whole stack on Cloudflare. Each permission maps to a concrete piece of your instance:
D1 · KV · R2 · Workers Scripts Your control plane — the database, login sessions, file & attachment storage, and the API + app Workers themselves.
Cloudflare Pages Hosts the web dashboard your team logs into.
DNS · Workers Routes Auto-creates the api., app., and per-workspace ws-… hostnames and points each at the right Worker.
SSL and Certificates Issues the Origin CA certificate so Cloudflare trusts the agent VMs you provision.
AI Gateway Created by the default deploy so SAM-managed AI and unified billing can be enabled without changing token permissions later.
Containers Required by the default Cloudflare Container instant-session runtime. Set CF_CONTAINER_ENABLED=false if you want to force VM runtime instead.
Workers Observability (read-only) Powers the in-app logs and error views so you can debug your own instance.
Zone (read-only) Lets Pulumi look up your domain. It never changes zone settings.
Analytics Engine (enabled separately, free) feeds SAM's usage and
cost dashboards and is used by the default deploy's analytics writes.
Step 4
Create your GitHub App
We'll pre-fill every setting using your domain. Click through to GitHub, paste the one
webhook secret we generate, and create the app.
Why your own GitHub App — and how agent permissions work
This GitHub App is yours — it's how SAM logs you in and how your
agents reach your code. Whenever an agent runs, SAM mints a short-lived token from
this App, so the App's permissions are the hard ceiling on what any agent can
ever do.
You can dial an individual agent down later in the SAM UI, but never
up past what the App grants. So enable everything you might want an
agent to do here, then restrict per-agent as needed. If there's a scope you never want
an agent to touch, leave it off the App entirely.
On the GitHub form, leave "Request user authorization (OAuth) during installation"unchecked. Checking it disables the Setup URL and breaks post-install redirects.
Exactly as it appears in the URL.
Pre-filled settings
Webhook secret
GitHub can't pre-fill this. Copy it into the Webhook secret field on the form:
We base64-encode this locally for GH_APP_PRIVATE_KEY.
The Client Secret and Private Key are never persisted to your browser storage — they live only in this tab until you close it.
Step 5
Generate a Pulumi passphrase
Pulumi encrypts your stack secrets with this passphrase. Add it to GitHub Actions in the next
step and keep a copy in your password manager for future deploys or teardown.
PULUMI_CONFIG_PASSPHRASE
Prefer to generate it yourself?
Run this in your terminal and use that value instead:
openssl rand -base64 32
Why a passphrase, and why save it?
Your Pulumi state holds secrets, so Pulumi encrypts them with this passphrase before
writing to R2 — anyone who reads the bucket still can't decrypt them.
There's no reset. Lose it and you can't update or tear down your stack, so save it in
your password manager now. Every future deploy must reuse the exact same value.
Step 6
Configure the production environment
In your fork: Settings → Environments → New environment → production.
Add the variables and secrets below. We've filled in everything you entered.
Environment variables
Environment secrets
Faster: set everything with the GitHub CLI
Already have GitHub CLI
installed and authenticated? Drop in your fork's repo and run one command to set every variable
and secret on the production environment at once.
Add your repo above and the command will target it directly. Without it, run the command from
inside a clone of your fork.
The command includes your secret values in plain text — run it in a trusted terminal and clear
your shell history afterward if you're cautious.
Security keys (ENCRYPTION_KEY, JWT_*), Origin CA credentials, and
TRIAL_CLAIM_TOKEN_SECRET are generated automatically by Pulumi — no manual setup.
GH_* secrets are mapped to GITHUB_* Worker secrets at deploy time.
Optional: Google OAuth (for GCP provider support)
Only needed if your users will deploy VMs on GCP. Add GOOGLE_CLIENT_ID and
GOOGLE_CLIENT_SECRET as environment secrets.
Why a GitHub Environment?
Your deploy pipeline reads its config from a named GitHub Environment rather than plain
repo secrets. That keeps your deploy credentials in one place you can lock down later
with required reviewers or branch protections — handy once more than one person can
push.
Variables are non-sensitive config (your domain, account IDs).
Secrets are credentials GitHub encrypts and never shows you again
after saving — so keep your own copy of anything you generated here.
Step 7
Deploy & verify
No commit needed — just run the Deploy Production workflow in your fork.
Open it, click Run workflow, and choose the main branch. Pulumi
provisions D1, KV, R2, and DNS, deploys the Worker and web UI, runs migrations, and builds
the VM agent.
Verify the API is healthy
Then open — you should see the login page.
What happens when you deploy?
One workflow run handles the whole pipeline: Pulumi provisions D1, KV, R2, and DNS,
deploys the Worker and dashboard, runs database migrations, and builds the VM agent
your workspaces boot from.
Re-running it later is safe — Pulumi compares your config to what's already live and
only applies what changed, so routine deploys are fast and won't touch untouched
resources.
That's it — you're self-hosting SAM.
Once your deploy finishes, log in to your own dashboard and create your first workspace.