Add broader infra sample services

This commit is contained in:
Chris Hendrickson 2026-05-05 00:24:38 -04:00
parent 9bc5779221
commit 397aed251f
38 changed files with 601 additions and 2 deletions

View file

@ -5,3 +5,12 @@ layout a project would use for local infrastructure.
Each service lives under `services/<service-id>/` and is discovered from its
`manifest.yaml`.
Included samples:
- `postgresql-18`: single PostgreSQL container with a named data volume.
- `valkey-9`: cache container backed by a Quadlet volume.
- `rustfs`: S3-compatible object storage on a Quadlet network and volume.
- `keycloak`: multi-container service with PostgreSQL on a shared network.
- `app-pod`: Podman pod with web and sidecar containers.
- `local-api-build`: local image build consumed by a container Quadlet.

View file

@ -0,0 +1,23 @@
# App Pod
Sample local pod service managed by `dew infra` and Podman Quadlets.
```bash
dew infra validate app-pod
dew infra up app-pod
dew infra status app-pod
dew infra logs app-pod --lines 100
```
This sample shows a Podman pod with a web container and a sidecar container.
- web endpoint: `http://127.0.0.1:8088`
- pod: `dew_app-pod`
- web container: `dew_app-pod-web`
- sidecar container: `dew_app-pod-sidecar`
Stop it with:
```bash
dew infra down app-pod
```

View file

@ -0,0 +1,17 @@
[Unit]
Description=Dew sample pod sidecar container
Requires=dew_app-pod.pod
After=dew_app-pod.pod
[Container]
Image=docker.io/library/busybox:1.37
ContainerName=dew_app-pod-sidecar
Pod=dew_app-pod.pod
Exec=sh -c "while true; do date; sleep 60; done"
[Service]
Restart=always
TimeoutStartSec=900
[Install]
WantedBy=default.target

View file

@ -0,0 +1,16 @@
[Unit]
Description=Dew sample pod web container
Requires=dew_app-pod.pod
After=dew_app-pod.pod
[Container]
Image=docker.io/library/nginx:alpine
ContainerName=dew_app-pod-web
Pod=dew_app-pod.pod
[Service]
Restart=always
TimeoutStartSec=900
[Install]
WantedBy=default.target

View file

@ -0,0 +1,6 @@
[Pod]
PodName=dew_app-pod
PublishPort=127.0.0.1:8088:80
[Install]
WantedBy=default.target

View file

@ -0,0 +1,19 @@
id: app-pod
name: App Pod
runtime:
type: podman-quadlet
quadlets:
- file: dew_app-pod.pod
unit: dew_app-pod-pod.service
- file: dew_app-pod-web.container
unit: dew_app-pod-web.service
container_name: dew_app-pod-web
- file: dew_app-pod-sidecar.container
unit: dew_app-pod-sidecar.service
container_name: dew_app-pod-sidecar
schemas:
configure: schemas/configure.schema.json
init: schemas/init.schema.json

View file

@ -0,0 +1,21 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://artificery.dev/dew/samples/app-pod/configure.schema.json",
"title": "App Pod Sample Configuration",
"description": "Configuration values represented by the sample pod Quadlets.",
"type": "object",
"additionalProperties": false,
"properties": {
"host_port": {
"type": "integer",
"minimum": 1,
"maximum": 65535,
"default": 8088
},
"pod_name": {
"type": "string",
"minLength": 1,
"default": "dew_app-pod"
}
}
}

View file

@ -0,0 +1,9 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://artificery.dev/dew/samples/app-pod/init.schema.json",
"title": "App Pod Sample Initialization",
"description": "Initialization values for the sample pod service.",
"type": "object",
"additionalProperties": false,
"properties": {}
}

View file

@ -0,0 +1,28 @@
# Keycloak
Sample local Keycloak service managed by `dew infra` and Podman Quadlets.
```bash
dew infra validate keycloak
dew infra up keycloak
dew infra status keycloak
dew infra logs keycloak --lines 100
```
This sample shows a multi-container service with an explicit Quadlet network and
a PostgreSQL dependency.
- Keycloak: `http://127.0.0.1:8080`
- admin user: `admin`
- admin password: `admin`
- database: `keycloak`
- database container: `dew_keycloak-postgresql`
- database volume: `dew_keycloak_postgresql_data`
Stop it with:
```bash
dew infra down keycloak
```
The PostgreSQL volume is intentionally retained after stopping the service.

View file

@ -0,0 +1,21 @@
[Unit]
Description=Dew sample Keycloak PostgreSQL
Requires=dew_keycloak.network dew_keycloak-postgresql.volume
After=dew_keycloak.network dew_keycloak-postgresql.volume
[Container]
Image=docker.io/library/postgres:18
ContainerName=dew_keycloak-postgresql
Network=dew_keycloak.network
NetworkAlias=postgres
Volume=dew_keycloak-postgresql.volume:/var/lib/postgresql/data
Environment=POSTGRES_DB=keycloak
Environment=POSTGRES_USER=keycloak
Environment=POSTGRES_PASSWORD=keycloak_dev_password
[Service]
Restart=always
TimeoutStartSec=900
[Install]
WantedBy=default.target

View file

@ -0,0 +1,2 @@
[Volume]
VolumeName=dew_keycloak_postgresql_data

View file

@ -0,0 +1,24 @@
[Unit]
Description=Dew sample Keycloak
Requires=dew_keycloak.network dew_keycloak-postgresql.container
After=dew_keycloak.network dew_keycloak-postgresql.container
[Container]
Image=quay.io/keycloak/keycloak:26.6.1
ContainerName=dew_keycloak
Network=dew_keycloak.network
PublishPort=127.0.0.1:8080:8080
Environment=KC_BOOTSTRAP_ADMIN_USERNAME=admin
Environment=KC_BOOTSTRAP_ADMIN_PASSWORD=admin
Environment=KC_DB=postgres
Environment=KC_DB_URL=jdbc:postgresql://postgres:5432/keycloak
Environment=KC_DB_USERNAME=keycloak
Environment=KC_DB_PASSWORD=keycloak_dev_password
Exec=start-dev
[Service]
Restart=always
TimeoutStartSec=900
[Install]
WantedBy=default.target

View file

@ -0,0 +1,2 @@
[Network]
NetworkName=dew_keycloak

View file

@ -0,0 +1,21 @@
id: keycloak
name: Keycloak
runtime:
type: podman-quadlet
quadlets:
- file: dew_keycloak.network
unit: dew_keycloak-network.service
- file: dew_keycloak-postgresql.volume
unit: dew_keycloak-postgresql-volume.service
- file: dew_keycloak-postgresql.container
unit: dew_keycloak-postgresql.service
container_name: dew_keycloak-postgresql
- file: dew_keycloak.container
unit: dew_keycloak.service
container_name: dew_keycloak
schemas:
configure: schemas/configure.schema.json
init: schemas/init.schema.json

View file

@ -0,0 +1,31 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://artificery.dev/dew/samples/keycloak/configure.schema.json",
"title": "Keycloak Sample Configuration",
"description": "Configuration values represented by the sample Keycloak Quadlets.",
"type": "object",
"additionalProperties": false,
"properties": {
"host_port": {
"type": "integer",
"minimum": 1,
"maximum": 65535,
"default": 8080
},
"admin_username": {
"type": "string",
"minLength": 1,
"default": "admin"
},
"database": {
"type": "string",
"minLength": 1,
"default": "keycloak"
},
"database_volume": {
"type": "string",
"minLength": 1,
"default": "dew_keycloak_postgresql_data"
}
}
}

View file

@ -0,0 +1,15 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://artificery.dev/dew/samples/keycloak/init.schema.json",
"title": "Keycloak Sample Initialization",
"description": "Initialization values for the sample Keycloak service.",
"type": "object",
"additionalProperties": false,
"properties": {
"realm": {
"type": "string",
"minLength": 1,
"default": "dew"
}
}
}

View file

@ -0,0 +1,8 @@
FROM docker.io/library/alpine:3.23
RUN mkdir -p /srv/www \
&& printf '{"service":"local-api-build","status":"ok"}\n' > /srv/www/index.html
EXPOSE 8080
CMD ["busybox", "httpd", "-f", "-p", "8080", "-h", "/srv/www"]

View file

@ -0,0 +1,23 @@
# Local API Build
Sample local image build managed by `dew infra` and Podman Quadlets.
```bash
dew infra validate local-api-build
dew infra up local-api-build
dew infra status local-api-build
dew infra logs local-api-build --lines 100
```
This sample shows a `.build` Quadlet that builds a local image from
`Containerfile`, then runs it through a `.container` Quadlet.
- endpoint: `http://127.0.0.1:8090`
- built image: `localhost/dew_local-api-build:latest`
- container: `dew_local-api-build`
Stop it with:
```bash
dew infra down local-api-build
```

View file

@ -0,0 +1,10 @@
[Build]
ImageTag=localhost/dew_local-api-build:latest
File=Containerfile
SetWorkingDirectory=unit
[Service]
TimeoutStartSec=900
[Install]
WantedBy=default.target

View file

@ -0,0 +1,16 @@
[Unit]
Description=Dew sample local API build
Requires=dew_local-api-build.build
After=dew_local-api-build.build
[Container]
Image=dew_local-api-build.build
ContainerName=dew_local-api-build
PublishPort=127.0.0.1:8090:8080
[Service]
Restart=always
TimeoutStartSec=900
[Install]
WantedBy=default.target

View file

@ -0,0 +1,16 @@
id: local-api-build
name: Local API Build
runtime:
type: podman-quadlet
quadlets:
- file: dew_local-api-build.build
unit: dew_local-api-build-build.service
- file: dew_local-api-build.container
unit: dew_local-api-build.service
container_name: dew_local-api-build
schemas:
configure: schemas/configure.schema.json
init: schemas/init.schema.json

View file

@ -0,0 +1,21 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://artificery.dev/dew/samples/local-api-build/configure.schema.json",
"title": "Local API Build Sample Configuration",
"description": "Configuration values represented by the sample local build Quadlets.",
"type": "object",
"additionalProperties": false,
"properties": {
"host_port": {
"type": "integer",
"minimum": 1,
"maximum": 65535,
"default": 8090
},
"image_tag": {
"type": "string",
"minLength": 1,
"default": "localhost/dew_local-api-build:latest"
}
}
}

View file

@ -0,0 +1,9 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://artificery.dev/dew/samples/local-api-build/init.schema.json",
"title": "Local API Build Sample Initialization",
"description": "Initialization values for the sample local build service.",
"type": "object",
"additionalProperties": false,
"properties": {}
}

View file

@ -0,0 +1,28 @@
# RustFS
Sample local RustFS object storage service managed by `dew infra` and Podman
Quadlets.
```bash
dew infra validate rustfs
dew infra up rustfs
dew infra status rustfs
dew infra logs rustfs --lines 100
```
This sample shows a container on an explicit Quadlet network with a named
Quadlet volume.
- S3 API: `http://127.0.0.1:9000`
- console: `http://127.0.0.1:9001`
- user: `rustfsadmin`
- password: `rustfsadmin`
- data volume: `dew_rustfs_data`
Stop it with:
```bash
dew infra down rustfs
```
The named volume is intentionally retained after stopping the service.

View file

@ -0,0 +1,23 @@
[Unit]
Description=Dew sample RustFS object storage
Requires=dew_rustfs.network dew_rustfs.volume
After=dew_rustfs.network dew_rustfs.volume
[Container]
Image=docker.io/rustfs/rustfs:latest
ContainerName=dew_rustfs
Network=dew_rustfs.network
PublishPort=127.0.0.1:9000:9000
PublishPort=127.0.0.1:9001:9001
Volume=dew_rustfs.volume:/data
Environment=RUSTFS_ACCESS_KEY=rustfsadmin
Environment=RUSTFS_SECRET_KEY=rustfsadmin
Environment=RUSTFS_CONSOLE_ENABLE=true
Exec=/data
[Service]
Restart=always
TimeoutStartSec=900
[Install]
WantedBy=default.target

View file

@ -0,0 +1,2 @@
[Network]
NetworkName=dew_rustfs

View file

@ -0,0 +1,4 @@
[Volume]
VolumeName=dew_rustfs_data
User=10001
Group=10001

View file

@ -0,0 +1,18 @@
id: rustfs
name: RustFS
runtime:
type: podman-quadlet
quadlets:
- file: dew_rustfs.network
unit: dew_rustfs-network.service
- file: dew_rustfs.volume
unit: dew_rustfs-volume.service
- file: dew_rustfs.container
unit: dew_rustfs.service
container_name: dew_rustfs
schemas:
configure: schemas/configure.schema.json
init: schemas/init.schema.json

View file

@ -0,0 +1,32 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://artificery.dev/dew/samples/rustfs/configure.schema.json",
"title": "RustFS Sample Configuration",
"description": "Configuration values represented by the sample RustFS Quadlets.",
"type": "object",
"additionalProperties": false,
"properties": {
"api_port": {
"type": "integer",
"minimum": 1,
"maximum": 65535,
"default": 9000
},
"console_port": {
"type": "integer",
"minimum": 1,
"maximum": 65535,
"default": 9001
},
"access_key": {
"type": "string",
"minLength": 1,
"default": "rustfsadmin"
},
"volume": {
"type": "string",
"minLength": 1,
"default": "dew_rustfs_data"
}
}
}

View file

@ -0,0 +1,15 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://artificery.dev/dew/samples/rustfs/init.schema.json",
"title": "RustFS Sample Initialization",
"description": "Initialization values for the sample RustFS service.",
"type": "object",
"additionalProperties": false,
"properties": {
"bucket": {
"type": "string",
"minLength": 1,
"default": "dew"
}
}
}

View file

@ -0,0 +1,24 @@
# Valkey 9
Sample local Valkey service managed by `dew infra` and Podman Quadlets.
```bash
dew infra validate valkey-9
dew infra up valkey-9
dew infra status valkey-9
dew infra logs valkey-9 --lines 100
```
This sample shows a container backed by a named Quadlet volume.
- host port: `127.0.0.1:6379`
- container: `dew_valkey-9`
- data volume: `dew_valkey-9_data`
Stop it with:
```bash
dew infra down valkey-9
```
The named volume is intentionally retained after stopping the service.

View file

@ -0,0 +1,18 @@
[Unit]
Description=Dew sample Valkey 9
Requires=dew_valkey-9.volume
After=dew_valkey-9.volume
[Container]
Image=docker.io/valkey/valkey:9.0.3-alpine
ContainerName=dew_valkey-9
PublishPort=127.0.0.1:6379:6379
Volume=dew_valkey-9.volume:/data
Exec=valkey-server --save 60 1 --loglevel warning
[Service]
Restart=always
TimeoutStartSec=900
[Install]
WantedBy=default.target

View file

@ -0,0 +1,2 @@
[Volume]
VolumeName=dew_valkey-9_data

View file

@ -0,0 +1,16 @@
id: valkey-9
name: Valkey 9
runtime:
type: podman-quadlet
quadlets:
- file: dew_valkey-9.volume
unit: dew_valkey-9-volume.service
- file: dew_valkey-9.container
unit: dew_valkey-9.service
container_name: dew_valkey-9
schemas:
configure: schemas/configure.schema.json
init: schemas/init.schema.json

View file

@ -0,0 +1,26 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://artificery.dev/dew/samples/valkey-9/configure.schema.json",
"title": "Valkey 9 Sample Configuration",
"description": "Configuration values represented by the sample Valkey Quadlets.",
"type": "object",
"additionalProperties": false,
"properties": {
"host_port": {
"type": "integer",
"minimum": 1,
"maximum": 65535,
"default": 6379
},
"volume": {
"type": "string",
"minLength": 1,
"default": "dew_valkey-9_data"
},
"snapshot_seconds": {
"type": "integer",
"minimum": 1,
"default": 60
}
}
}

View file

@ -0,0 +1,9 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://artificery.dev/dew/samples/valkey-9/init.schema.json",
"title": "Valkey 9 Sample Initialization",
"description": "Initialization values for the sample Valkey service.",
"type": "object",
"additionalProperties": false,
"properties": {}
}

View file

@ -0,0 +1,8 @@
---
id: DEW-0035
title: Add broader infra sample services
type: task
created: 2026-05-05T04:19:43.484054Z
---
Add sample infrastructure services that exercise common Podman Quadlet patterns including volumes, networks, pods, multi-container dependencies, and local image builds.

View file

@ -85,8 +85,15 @@ declared units.
The Dew repository includes sample service bringups under
`.project/infrastructure/services/`.
`postgresql-18` brings up a local PostgreSQL 18 container through Podman
Quadlets:
Available samples:
- `postgresql-18`: single PostgreSQL 18 container with a named data volume.
- `valkey-9`: cache container backed by a Quadlet volume.
- `rustfs`: S3-compatible object storage on a Quadlet network and volume.
- `keycloak`: multi-container Keycloak and PostgreSQL service on a shared
network.
- `app-pod`: Podman pod with web and sidecar containers.
- `local-api-build`: local image build consumed by a container Quadlet.
```bash
dew infra validate postgresql-18