Self-hosted SaaS boilerplate: what "self-hosted" has to mean, and a checklist
"Self-hosted" has become a checkbox on boilerplate landing pages, and the
checkbox is usually true in the narrowest sense: yes, the Node process can
run on your VPS. Then you read the .env.example and find the auth
provider's API key, the managed database URL, the queue service token, the
email API key and the file-storage bucket. The process is yours; the
product is theirs. This article proposes a definition strict enough to be
useful, and turns it into questions you can ask any kit — including ours.
A definition
A SaaS foundation is self-hosted when the product keeps working, for all its users, with nothing but the servers you operate and the one payment processor you cannot avoid. Everything else is optional or replaceable with something you run:
- Sign-in works with your database alone. External identity providers are a feature you add, not a dependency you start with.
- Email goes out through any SMTP server, including your own; a hosted email API is a configuration, not a requirement.
- Background jobs run inside the application, backed by your database, and survive a restart.
- Files go to a local directory or to any S3-compatible service you choose, including one you run.
- Analytics, error reporting and monitoring are opt-in and can point at self-hosted tools.
- The payment processor is the exception, and its failure mode is bounded: with no key configured, the application runs, and billing is simply off.
By that definition most "self-hostable" kits are not. That is not an accusation; it is a consequence of building on a BaaS. It is worth knowing before you sign a data-processing agreement that lists sub-processors you did not choose.
Why it matters, concretely
The bill. A rented stack is five invoices with five floors. A binary on a server is the server. We priced it in The rented stack.
The questionnaire. The first enterprise customer sends a security questionnaire. "Where is the data, who are the sub-processors, how do you delete a user?" With a self-hosted foundation the answers are one country, three names and a button. See Keeping a SaaS in the EU for what self-hosting settles and what it does not.
The exit. When a vendor changes pricing or terms, a self-hosted product moves by copying a binary and a database dump. A product built on managed auth moves by re-implementing auth.
The checklist
Ask these of any kit. The honest answers are usually in the .env.example.
- Delete every third-party key from the configuration. Does it boot, can a user sign up, sign in, reset a password? If sign-in needs an external provider to exist, it is not self-hosted.
- Which database, and can I run it myself? PostgreSQL on your server counts. A vendor's "serverless Postgres" with a connection string does not, unless the kit also runs against a plain instance.
- Where do background jobs run? In-process with a database table: yes. A hosted queue with a token: no.
- How does email leave? SMTP to any host: yes. Only through one API vendor: no.
- Where do uploads go? Local disk or any S3-compatible endpoint: yes. One vendor's bucket API: no.
- What happens with no payment key? The app should run and hide billing. A crash, or a checkout button that errors, means the unconfigured path was never tested.
- What runs in production? One process you can list is better than a platform-specific build output. Count the runtimes: Node, PHP, Redis, workers.
- Can I read all of it? Lines of code you will own, not features advertised. If the answer is "it's a monorepo, you don't need to read the packages", you are renting with extra steps.
- Is the licence perpetual, and does it cover client work?
- Does the repository come with a way to run everything locally, including the external pieces (identity provider, S3, mail, payment sandbox)? If the vendor's own demo needs their cloud, so will yours.
How GoVueKit answers
- Boots and signs users up with a database file and nothing else: argon2id passwords, verification, reset, magic links, TOTP, session revocation. Google, GitHub, Microsoft and any OIDC provider are switched on by adding keys; the unconfigured path is tested in CI.
- PostgreSQL and SQLite, same migrations, same generated queries,
both engines in the test matrix.
DB_DRIVERdecides. - Jobs are rows in your database, run in-process, retried, surviving redeploys.
- SMTP by default; Resend is an option behind the same
Mailerinterface. STORAGE=diskorSTORAGE=s3against any endpoint (the labs uses MinIO).- No Stripe key means billing is off and the app is otherwise complete; the tests cover that configuration because it is the one most deployments run.
- One static binary, ~27 MB, the Vue app embedded. Traefik for TLS if you want automatic certificates. Nothing else.
- About 11 000 lines of Go, 19 packages, one migration file, and a
DECISIONS.mdthat explains the non-obvious choices — public before you buy, at github.com/kOlapsis/govuekit. - €199 once, perpetual, lifetime updates of the major version, unlimited end products including client work.
- One compose file, no account:
curl -fsSLO https://govuekit.dev/labs/docker-compose.yml
docker compose up -d # app + PostgreSQL + Dex (OIDC) + MinIO (S3) + stripe-mock + Mailpit
The tenth question is the one to start with. A kit that can boot its whole world on your laptop can boot it on your server; one that cannot has already told you where it really lives.