Cross-compiles to a lean executable, runs as a systemd service. No container runtime, no managed-cloud tax. Your AWS account, your data, our orchestration.
Not on Rust? Python and static sites deploy the same way →
Connecting your AWS account takes more setup than a managed cloud, but you get infra that's actually yours. Here's how we keep that setup from becoming ongoing friction.
Run cumulus aws connect to grant a scoped cross-account role. One CloudFormation template. Done once per account.
Drop a cumulus.toml in your repo root. Pick your runtime and target. Most defaults are sensible.
Run cumulus app deploy. Cumulus builds your app on an ephemeral Fargate task in your account, uploads the artifact, and runs a health-gated deploy with automatic rollback.
Connect your GitHub repo. Every push to your deploy branch builds, deploys, and streams logs to your dashboard.
One config file. No Dockerfile to maintain. Cumulus builds your binary on an ephemeral Fargate task in your own AWS account. The output is a stripped executable deployed as a systemd service.
# Drop this in your repo root
[project]
name = "my-api"
region = "us-east-1"
[[app]]
name = "api"
runtime = "rust"
target = "ec2"
binary = "my_api"
server = "prod-1"
[app.health_check]
path = "/health" # probed before cutover;
port = 8080 # a non-2xx rolls back
[app.env]
# Resolved from SSM at deploy, never baked into the artifact
DATABASE_URL = { secret = "/my-api/DATABASE_URL" }# Install the CLI
$ curl -fsSL https://cumulus.collectif.dev/install.sh | sh
# Sign in (opens your browser)
$ cumulus login
# Connect your AWS account (once)
$ cumulus aws connect
# Provision a server
$ cumulus server create prod-1
# Register the project + apps from cumulus.toml
$ cumulus link
# Store the secret in SSM, encrypted with your KMS key
$ cumulus app env-set my-api/api DATABASE_URL="postgres://…" --secret
# Deploy (defaults to the current commit)
$ cumulus app deploy my-api/api→ deploying `api` (Rust → EC2) [env: production] • pre-flight passed • building on Fargate (arm64)… • artifact uploaded • agent installed the systemd service • health check passed (200 /health) • cut over, zero downtime • decommissioned old release ✓ deployed `api` to `prod-1`
Shuttle.rs closed its Pro tier in January 2026. If you're looking for a permanent home for your Rust backend, Cumulus is the natural migration path, and you end up with more durable infrastructure than you started with.
Your service runs as a native binary under systemd on an EC2 instance in your own AWS account. No managed-cloud dependency, no container overhead, no vendor-specific framework required. Bring your existing Axum, Actix, or any other Rust web service as-is.
Prefer serverless? The same Rust binary deploys to AWS Lambda instead. It runs on Graviton (arm64) or x86_64, behind a Function URL, with instant rollback to any previous version.
And if Cumulus ever shut down tomorrow, your API would still be serving traffic. The EC2 instance and the systemd service are yours, and they have no runtime dependency on our control plane.
Read the quickstart →# Before: Shuttle annotations
# #[shuttle_runtime::main]
# async fn main() -> ShuttleAxum { ... }
# After: a standard Tokio main, deployed by Cumulus
[[app]]
name = "api"
runtime = "rust"
target = "ec2" # or "lambda"
binary = "api"
server = "prod-1"
[app.env]
DATABASE_URL = { secret = "/api/DATABASE_URL" }Every combination that makes sense. Mix runtimes and targets in one cumulus.toml: a Rust API, a Python worker, and a Next.js frontend, all in one project.
| Runtime | Target | Build | How it runs |
|---|---|---|---|
| Rust | EC2 | Cross-compiled to a native binary on an ephemeral Fargate task, with no CI toolchain to maintain | Native systemd service, not a container. Health-gated cutover, 5-release rollback history, zero-downtime swap. |
| Rust | Lambda | Compiled for the Lambda custom runtime | arm64 (Graviton) or x86_64, behind a Function URL. Instant rollback to any previous version, no redeploy needed to revert. |
| Python | EC2 | Source tarball + uv-managed venv built on the server | uvicorn, gunicorn, or a raw process. uv.lock, poetry.lock, Pipfile.lock, or requirements.txt all work. |
| Python | Lambda | Dependencies installed into a Lambda-matching container, zipped | Managed Python runtime. uv / poetry / pipenv / pip all work as dependency sources. |
| Static | S3 | Node build on Fargate, then s3 sync | Optional CloudFront CDN + ACM cert + Route 53 for a custom domain (one-command provisioning landing soon). |
Every resource (EC2, Lambda, S3, SSM, RDS) lives in your AWS account. Cumulus assumes a scoped cross-account role constrained to cumulus-* resources with a unique ExternalId. Nothing runs in ours, and your apps keep serving traffic whether or not you're using Cumulus.
Every deploy probes your app's /health endpoint before cutting over. A non-2xx response triggers automatic compensating rollback, and the old release keeps serving while we clean up.
A failed health check rolls the deploy back automatically. To revert on purpose, redeploy a known-good commit — cumulus app releases lists the last five retained releases, then cumulus app deploy <app> --sha <good>. Lambda reverts by re-targeting the alias, so it's instant.
Env vars live in SSM Parameter Store in your account, KMS-encrypted with your CMK. Values are fetched at deploy time and written to disk at mode 0600. They never touch build artifacts.
Deploy to staging and production from the same config. Promote a known-good SHA between environments with cumulus app promote: no rebuild, same artifact.
Connect a GitHub repo. Every push to your configured branch triggers a build and deploy, with logs streaming live to your dashboard via SSE.
Tail journald logs from any deployed app with cumulus app logs. Streamed in real time. No SSH required, no agent port exposed to the internet.
Invite members with Owner, Admin, Member, Billing, or Viewer roles. Sign-in is GitHub OAuth; API tokens for the CLI are org-scoped and stored only as a hash. The control plane never keeps the plaintext.
ACM certificates, CloudFront (static sites) or an ALB (EC2 apps), and Route 53 records for your own domain. One-command provisioning on deploy is landing soon.
The short answer: only what you grant, only on resources it creates. Here's the full picture.
You deploy a CloudFormation template and see exactly what it permits before accepting. Lambda functions, S3 buckets, SSM parameters, and IAM roles are constrained by cumulus-* name prefixes at the IAM level. EC2 and CloudFront actions are included for server provisioning, and you can review the full policy in the template.
Your account connection is issued a cryptographically random ExternalId embedded in the CloudFormation template. Every AssumeRole call must present it. This prevents confused-deputy attacks where Cumulus could be tricked into acting on another customer's account.
All managed EC2 instances require IMDSv2 (HttpTokensRequired). IMDSv1 is disabled. The hop-limit of 1 blocks container-to-host SSRF. A container on the instance cannot steal instance role credentials from the metadata service.
All encrypted resources (S3 artifacts, SSM parameters, and RDS) use customer-managed keys. No AWS default managed keys (aws/s3, aws/rds). Revoking your CMK revokes Cumulus's ability to read or write encrypted data, independently of IAM.
Cumulus assumes the role under sessions named cumulus-<operation>-<id>. Every AWS API call it makes in your account appears in your own CloudTrail under that session name. No side channel, no blind spots.
Every resource Cumulus creates carries ManagedBy: cumulus and uses the cumulus- name prefix. Use your AWS Cost Explorer or Config to audit exactly what's under Cumulus's management, with no guesswork.
A multi-service app across two environments bills as many separate services on per-service platforms, often over $150/mo in platform fees before your first AWS dollar. Here it's one flat number. You pay AWS directly; Cumulus charges for the management plane only.
All plans include unlimited deployments, rollbacks, and log streaming. AWS costs go directly to your AWS bill, and Cumulus never marks them up.
Bring-your-own-AWS alternatives bill per service: each web server, background worker, static site, or database, counted separately, then multiplied by every environment.
Scenario: API + background worker + frontend + Postgres, across staging and production, at roughly $20/service/mo.
Both apps share one server per environment, the frontend goes to S3 + CloudFront, and Postgres runs on RDS, all under one flat plan. Staging and production don't multiply the fee.
AWS costs are billed directly to you, never marked up. The flat fee wins once your app has more than two or three components.
Start free on the Hobby plan. No card required. Your infra stays in your account whether you use Cumulus or not.