Services (managed addons)

Add managed services (Postgres, Redis, MongoDB, Mailpit, Meilisearch, MinIO) to a project's environment from a built-in catalog. Each service runs as a container on the paas-apps network with a persistent named volume; apps in the same environment automatically receive its connection URL as {SLUG}_URL in their runtime env vars.

Catalog

Key Service Image Default port Volume?
postgres-16 PostgreSQL 16 postgres:16-alpine 5432 yes
redis-7 Redis 7 redis:7-alpine 6379 yes
mongodb-7 MongoDB 7 mongo:7 27017 yes
meilisearch-1.10 Meilisearch getmeili/meilisearch:v1.10 7700 yes
minio MinIO (S3) minio/minio:latest 9000 yes
mailpit Mailpit (SMTP) axllent/mailpit:latest 1025 yes

To add more, edit src/Paas.Domain/Catalog/Catalog.cs and rebuild. The catalog is searchable by name/description/category/tags via the dashboard and the API: GET /api/service-templates?q=<query>.

Adding a service

Dashboard

Sidebar → Services → pick env tab → + Add service. Search the catalog ("redis", "search", "data" all work), pick a template, give it a slug (db, cache, search...), click Provision.

The service moves through Provisioning → Ready (typically 5–15s). When Ready, click Reveal DSN to see the connection string and the env-var name it will be auto-injected as.

API

TOKEN=…
PROJ=https://paas.example.com/api/orgs/acme/projects/storefront

# Provision a Redis named "cache" in production
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
     -d '{"slug":"cache","templateKey":"redis-7"}' \
     "$PROJ/envs/production/services/"

# List services in an env
curl -H "Authorization: Bearer $TOKEN" "$PROJ/envs/production/services/"

# Reveal connection string (admin only, audited)
curl -H "Authorization: Bearer $TOKEN" \
     "$PROJ/envs/production/services/cache/dsn"

How apps connect

For every Ready service in (project, env), the Orchestrator injects an env var into every app in the same env on the next deploy:

{SLUG_UPPERCASE}_URL = <rendered DSN>

Slug cache (Redis) → CACHE_URL=redis://...:6379. Slug db (Postgres) → DB_URL=postgres://app:<password>@...:5432/db.

Hyphens in slugs become underscores: feature-flagsFEATURE_FLAGS_URL.

Apps should read these vars at startup. They're populated on the next deploy or redeploy of the app — adding a service does not auto-restart running apps. Either redeploy explicitly or push a new commit.

Per-env isolation

Services are scoped to a single (Project, Environment). Adding a Redis to Production does not create one in Staging. Each env runs its own instances with its own data volumes — staging cannot accidentally see prod data.

If you want the same template across multiple envs, provision it explicitly in each.

Lifecycle

Action Effect
Add Container starts, named volume created, status → Ready.
Reveal DSN Decrypts the stored config, renders DSN. Audit-logged.
Delete Container stops + removed. Volume preserved by default so you can re-create the service and recover its data.
Auto-restart on host reboot Containers use restart=unless-stopped so they come back up automatically.

There is no auto-failover or HA in v1 — these are single-container instances ideal for self-hosted-side projects and staging environments. For prod-grade HA, use a managed cloud provider for stateful tier and let Paas keep managing your apps.

Storage paths

Each service has a named volume paas-svc-<id> mounted at the template's VolumeMountPath. To back up Postgres for example:

docker exec paas-svc-<id> pg_dump -U app db | gzip > db-$(date +%F).sql.gz

To list all service volumes:

docker volume ls --filter name=paas-svc-

Custom services

Not yet — v1 only ships with the built-in catalog. The data model supports arbitrary templates (any image, any env spec, any DSN format), so a v1.x addition will let users define their own templates via JSON in the dashboard.