Alpha · reproducible self-hosted infrastructure

Stacks should compile.

Choose the services. Build the release. Verify the host.

Put the services you want in one site manifest and pin every module to an exact revision. Platform Zero checks the source, builds a secret-free release, renders its Podman and systemd runtime on the target host, and verifies the deployed contract.

Open source · exact module pins · secret-free build · isolated runtimes · verified activation · release rollback

Alpha software: the architecture works on the project’s test stack, but interfaces and module contracts are still changing. Expect hands-on setup, debugging, and upgrades.

What Platform Zero actually does

Turn one site manifest into a secret-free deployment release.

It is a build and deployment system, not a fixed bundle of apps.

The operator path starts with build.sh. It resolves the site’s pinned modules, merges their catalogues and runtime contracts, runs source checks, and writes a secret-free release under dist/ without changing the target host.

On the target host, deploy.sh decrypts the site inputs, renders runtime configuration, validates the Podman bundle, installs generated systemd user units, and reconciles the requested target. verify.sh then checks the deployed contract.

Where each decision belongs

Shared code stays reusable. Each deployment keeps its own details.

When something changes, there is a clear place to change it: generic assembly lives in the generator, service behavior lives in a module, and the choices for one deployment live in its site configuration.

01 / shared engine

Generator

Resolves module pins, merges catalogues and runtime files, enforces source policy, and builds the deployable release.

build.sh → checked source → dist/
02 / one service

Modules

Package one service or a related group: its runtime contract, configuration, container context, dependencies, and module-owned tests.

stack.runtime.yaml + config + tests
03 / one deployment

Site config

Selects modules, pins exact revisions, carries encrypted deployment inputs, and identifies the target environment.

selection + pins + encrypted values
  1. 1

    Select

    List the modules in the site manifest, record exact revisions, and keep deployment inputs encrypted.

  2. 2

    Build

    Resolve the pins, merge module contributions, enforce source checks, and write the secret-free release locally.

    ./build.sh --manifest /path/to/site/manifest.json
  3. 3

    Deploy

    Sync dist/ to the host; decrypt, render, validate, install, and reconcile the requested runtime there.

    cd ~/webservices && ./deploy.sh
  4. 4

    Verify

    Check readiness and the deployed runtime contract, then run focused or complete service and browser suites as needed.

    ./verify.sh

Why Podman fits this model

Per-user boundaries are a good fit for Podman.

The current alpha runs ordinary apps and container-creating tools in separate rootless operating-system accounts. Podman is a good match for that layout: containers do not require one central privileged daemon, while an API socket can be added inside only the user domain that needs it.

Typical shared Docker layout

Often one socket, one daemon, and many workloads.

In a common Docker setup, access to the main socket can mean broad control over the daemon and what it can reach. If CI, notebooks, tests, and long-lived apps share that control plane, the potential blast radius grows.

Platform Zero + Podman

Separate OS users for separate trust domains.

In the generated layout, each socket controls its owning user’s rootless runtime. UNIX permissions, separate subordinate-ID ranges, storage, and user systemd managers are used to keep those control planes apart.

01 / contain control

One socket does not automatically grant stack-wide control.

A notebook or CI job can create containers without receiving the application socket or rootful control plane. This limits exposure to that service account’s host permissions and rootless runtime.

02 / remove the middle

Systemd manages the lifecycle directly.

Quadlet turns generated container definitions into systemd units. Services start, stop, log, fail, and restart through the host’s existing supervisor rather than depending on one always-on container daemon.

03 / add APIs narrowly

API-compatible sockets stay scoped to their owners.

Tools such as JupyterHub and Forgejo Runner can use a Docker-compatible Podman API. Where needed, Platform Zero enables a separate, socket-activated API service for the owning user instead of sharing one socket across the stack.

The precise comparison: this is not a claim that Podman uniquely makes isolation possible. Docker can also be configured with rootless or multiple daemons. Podman’s daemonless architecture simply fits Platform Zero’s generated per-user model well. These boundaries reduce blast radius; they do not replace careful permissions, updates, or review of the remaining rootful services.

Security follows the deployment

Most services run in rootless Podman domains. Host access is an explicit exception.

Platform Zero does not put every container behind one privileged socket. Placement, identity, validation, secret delivery, and rollback are explicit parts of the generated bundle, so the important boundaries can be reviewed before activation.

01 / placement

Most applications do not run as root.

Modules declare whether they need host access. Ordinary applications run in the generated rootless domain; rootful placement remains a visible exception for infrastructure that actually needs it.

rootful only when host access is required
02 / isolation

Workload managers get their own Podman sockets.

JupyterHub, Forgejo Runner, managed test runners, and Workload Spawner each have a separate user, runtime directory, release root, and Podman socket.

webservices ≠ jupyterhub ≠ test-runners ≠ forgejo-runner ≠ workload-spawner
03 / identity

Single sign-on stops at a defined edge.

OIDC-capable services connect to Keycloak. Proxy-auth services accept identity headers only from configured proxy ranges, and enabled clients must have the credentials they require.

Keycloak clients + explicit trusted proxy CIDRs
04 / secrets

Plaintext secrets stay out of the generated bundle.

Encrypted site inputs can remain bundled. The installer can render them before activation or accept a separate directory of final per-service environment files, then installs restricted copies into each owning domain.

encrypted inputs or separate env files → restricted runtime files
05 / preflight

Validation does not have to change the host.

Without --activate, the installer checks the bundle manifest, environment files, Quadlets, and generated units, then exits before installation.

validate first → activate deliberately
06 / recovery

Activation and updates keep rollback paths.

Versioned releases switch through current links, and failed activation restores the previous release. Eligible registry-managed daemons update with Podman rollback and freeze further updates after a failed run.

versioned activation + guarded auto-update

The result is inspectable: stack.ir.json shows placement and dependencies; the generated directories show exactly which Quadlets, users, sockets, and releases will be installed.

A release must earn activation

The safety path is executable, not implied.

Source policy, artifact provenance, deployment durability, and identity behavior are checked at the boundaries where a plausible-looking release can still fail.

Capabilities you can compose

Pick the services your workflow needs—and leave the rest out.

Start with the capabilities people need: shared files, chat, monitoring, notebooks, code, search, or automation. The site manifest includes the modules you select and the dependencies they require. If a needed service is missing, package its repository with the same module contract instead of burying another custom install in the host.

Examples from a running test deployment

A few cross-service workflows in the current test stack.

Monitoring, automation, alerts, team response, publishing, and search are separate products here, but they share one operator-controlled stack. These screenshots show the current alpha in useful, populated states rather than login pages or empty dashboards.

A good fit—and when it is not

Platform Zero is for teams running several self-hosted services together.

It earns its maintenance cost when you need control, reproducibility, and workflows that cross product boundaries. If one hosted product already covers the job, use the simpler product.

Platform Zero fits when

  • You need several open-source applications under one identity and deployment model.
  • A real workflow crosses tools—for example, a health check triggers automation, an alert, and a team response room.
  • You need exact source pins, inspectable runtime output, validate-first activation, and a rollback path.
  • You maintain private services without wanting to fork the shared generator.

Choose something simpler when

  • One hosted application already handles the complete workflow.
  • No one on the team can own operating-system updates, backups, restores, and service upgrades.
  • The integrations would create more failure modes than manual work they remove.

Sketch the first stack

Describe what people need to do, from start to finish.

Write down who will use the system, what they need to do from start to finish, which services or data must remain, and the constraints that matter. That is enough to choose a first set of modules and see where custom work is actually needed.

Stack sketch

A useful first pass can be six lines. It does not need to name every container.

Read the generator source