Docs / Self-hosting
Eject and self-host
Alleex Cloud generates standard Next.js code. You can eject your app at any time, take the code, and run it on your own infrastructure. The eject boundary is defined in @alleex-cloud/canonical-schema — it is a machine-readable contract, not a policy document.
The model
What eject does and does not do
Ejecting relocates the boundary between Alleex Cloud-hosted infrastructure and your app code. The Alleex Cloud builder provisions infrastructure for you (Neon database, Cloudflare Workers, Infisical secrets, Trigger.dev compliance crons). When you eject, that provisioning automation is stripped. Your app code and all @alleex-cloud/* modules are fully portable — they leave with you.
Eject is not a downgrade. The generated app is production-grade Next.js. You gain full control of infrastructure at the cost of managing it yourself. Alleex Cloud continues to work for new apps; the ejected app is yours independently.
The eject boundary is defined in packages/canonical-schema/src/eject/boundary.ts (changelog ref dc0a16b). It is the authoritative, machine-readable definition of what the CLI strips and what it keeps. This page explains it in plain language.
The boundary
What the eject command strips
The eject tool removes exactly the files and env keys listed in the boundary manifest — nothing more, nothing less. These are the parts that only make sense when Alleex Cloud hosts your app:
Files removed from the ejected fork
src/lib/control-plane/src/lib/mcp-spine/src/lib/spine/src/db/audit-triggers.generated.sqlsrc/jobs/rekor-witnesssrc/jobs/compliance-cronsrc/lib/registry-phone-homeThese paths are the hosted MCP spine, control-plane provisioning glue, the managed Rekor-witness cron, managed compliance jobs, and the registry phone-home. Your app code, module code, schema, and UI are untouched.
Env keys dropped (never written to .env.example)
MORTAR_CONTROL_PLANE_URLMORTAR_CONTROL_PLANE_TOKENNEXT_PUBLIC_CONTROL_PLANE_URLNEON_API_KEYNEON_ORG_IDCLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_IDINFISICAL_CLIENT_IDINFISICAL_CLIENT_SECRETINFISICAL_PARENT_PROJECT_IDINFISICAL_BASE_URLTRIGGER_API_KEYTRIGGER_PROJECT_REFVERCEL_TOKENINTERNAL_API_TOKENThese are the keys that authorize the control plane to talk to Neon, Cloudflare, Infisical, Trigger.dev, and Vercel on your behalf. Self-hosted apps manage their own credentials.
What leaves with you
What the ejected app keeps
The following @alleex-cloud/* packages are pinned or vendored into the ejected fork — they are portable and do not require Alleex Cloud to run:
Beyond these packages, everything that was generated into your app is standard Next.js and Drizzle ORM code. There are no hidden runtime dependencies on Alleex Cloud infrastructure after eject — the boundary strips the provisioning glue and that is all.
The secret-provider import is rewritten from the hosted Infisical adapter (@alleex-cloud/secret-infisical) to a local dotenv shim (./lib/secrets-dotenv) automatically during eject. You can replace this shim with any secret manager you choose.
src/jobs/rekor-witness) is stripped on eject. Your app still has its local hash-chained audit table and trigger — the chain is intact. You are responsible for running your own Rekor-witness job if you want continued third-party witnessing of chain heads.Runbook
Self-hosting in 7 steps
This is a condensed version of the full docs/HOSTING.md runbook. Steps marked human require you to sign up for accounts, accept terms of service, or make DNS changes — an agent cannot do these on your behalf.
Run the eject command
From the Alleex Cloud builder dashboard or CLI: `mortar eject --app <app-id> --out ./my-app`. The eject tool reads the boundary manifest, strips control-plane glue, rewrites the secret-provider import from Infisical to a local dotenv shim, and writes a clean Next.js project to the output directory.
Create your own GitHub repository
Push the ejected directory to a new GitHub repository you own. This is your codebase. Alleex Cloud does not retain write access to it. You own the code from this point forward.
Create a Neon Postgres project in the EU
Sign up at neon.tech, create a project in the Frankfurt region, and copy the pooled connection string. This replaces the Neon project Alleex Cloud would have provisioned for you. Run migrations: `DATABASE_URL=<url> pnpm db:generate && pnpm db:migrate`.
Create a Cloudflare account for static hosting and R2
Sign up at cloudflare.com. If your app uses file storage (R2) or EU-edge delivery (Workers), create the relevant resources. Point your domain's DNS to Cloudflare. Cloudflare and Neon both offer EU data residency — self-hosting does not automatically move you to a non-EU provider; that is a choice you make when picking services.
Set your own environment variables
Copy `.env.example` from the ejected project. Fill in your own values: `DATABASE_URL`, `BETTER_AUTH_SECRET`, `BETTER_AUTH_URL`, and any module-specific keys (e.g. `STRIPE_SECRET_KEY`, `COMPLIANCE_DSAR_SIGNING_KEY`). Do not commit real values to git — use your hosting provider's secret store.
Deploy to your chosen platform
The ejected project is a standard Next.js app. Deploy to Vercel, Cloudflare Pages, or any platform that supports Next.js. Point the platform at your GitHub repository. Set the env vars in the platform's dashboard, not in your repo.
Configure your domain and DNS
Point your domain at the deployed app. Your hosting platform issues TLS. Accounts, payment, and DNS changes are irreducibly human steps — an agent cannot sign up for services or accept terms on your behalf.
Data residency
EU residency is your responsibility
When Alleex Cloud hosts your app, EU data residency is enforced by construction: Neon projects are created in Frankfurt, Cloudflare Workers run in EU PoPs, and no app data leaves the EU.
When you self-host, you choose your own infrastructure. Picking a US-based Postgres provider or a non-EU hosting platform means your app's data residency is determined by your choices, not by Alleex Cloud. The generated code does not enforce this after eject — the compliance obligation is yours.
If EU residency matters for your use case (it likely does — that is why you used Alleex Cloud), use Neon with a Frankfurt project and Cloudflare with EU Workers. Both are straightforward to configure and are the same providers Alleex Cloud uses internally.
The compliance-eu module's DSAR portal, audit log, and cookie consent continue to function correctly in self-hosted mode — they are app code, not hosted services. Your obligation to respond to DSAR requests within 30 days remains; the generated portal still handles the mechanics.
Honest scope
What self-hosting does not include
Automatic provisioning
The control plane provisions Neon projects, Cloudflare Workers, and Infisical secret stores automatically for hosted apps. Self-hosted apps provision their own infrastructure manually (or with their own automation).
Custom domains via Alleex Cloud
Hosted apps currently get *.workers.dev URLs; custom domain support is in progress. Self-hosted apps configure their own domain through their hosting provider.
Managed Rekor witnessing
The hosted plan runs a cron that witnesses audit-log chain heads in Sigstore Rekor. This cron is stripped on eject. You can run your own witness job using the Rekor API — it is a public transparency log.
Managed compliance crons
Overdue DSAR tracking and compliance-cron jobs run in Trigger.dev on the hosted plan. Self-hosted apps are responsible for running their own equivalent scheduled jobs.
Future Alleex Cloud-hosted MCP spine
The hosted MCP spine is stripped on eject. If you want MCP tooling for your self-hosted app, you run your own MCP endpoint. The module manifests describe the available tools; the spine code is in the stripped paths.
This page describes the technical eject boundary and a condensed runbook. It is not legal advice and does not substitute for your own assessment of data protection obligations.
Start hosted, eject when you're ready
The fastest path is to deploy from the gallery and let Alleex Cloud handle the infrastructure. You can eject at any point — the boundary is there from day one.