Files
orca/examples/full-stack
Jon Chery 7dc7980d74 docs(E): UAT docs + signoff script fixes + pve-ct example (REQ-170)
- docs/uat.md: remove --rp-id from cluster seal (belongs to auth init-idp);
  fix secrets set syntax (positional KEY=value, not --value flag); add
  auth init-idp step; add troubleshooting section (ORCA_HOME, known_hosts,
  Traefik, SSH, job list, Proxmox runtime)
- scripts/uat-signoff.sh: fix 6 assertions (#04 SKIP if no linux, #08
  check node field in JSON, #14 verify file exists first, #27 fix pprof
  grep, #34/35 already passing); add 3 new assertions (#36 traefik
  installed, #37 known_hosts exists, #38 master_key exists); total 38
- examples/full-stack/web-app-lxc.md: pve-ct jobspec variant for Proxmox
  LXC container deployment

---ci---
project: orca
milestone: v0.12.18
phase: E
status: complete
requirements:
  covered: [170]
---/ci---
2026-08-10 16:37:57 +00:00
..

Full-Stack Example with Ingress

This directory contains a complete multi-service stack deployed with Orca, including Traefik ingress configuration. Each file is a valid Orca jobspec (.md frontmatter) that passes the v0.9 parser and schema validators.

Runnable out-of-the-box: The runtime.command in each example uses /bin/sleep 3600 (for long-running services) or /bin/echo (for one-shot jobs) so that orca job run <file>.md succeeds on any Linux machine without installing any software. Each file has a Production substitution note showing the real binary to use in a deployment (e.g. /usr/bin/httpd, /usr/lib/postgresql/16/bin/postgres).

Stack overview

File Kind Runtime Ingress Description
web-app.md Service process Unix socket (default) Frontend HTTP server, 3 replicas, rolling update
api.md Service process TCP 127.0.0.1:9090 (R-007 opt-in) Backend API, 2 replicas, canary update
worker.md Job process none One-shot batch worker with lifecycle hooks
log-shipper.md Service process Unix socket (metrics) Log shipper on a dedicated node
postgres.md Service process Unix socket Database with volume replication, blue-green update

Rendered artifacts

The rendered/ directory shows what Orca generates on the target nodes when you submit these jobspecs:

File Description
traefik-dynamic-web-app.yaml Traefik dynamic config for the web-app Service
traefik-dynamic-api.yaml Traefik dynamic config for the api Service (TCP bind)
systemd-web-app.service Systemd unit for the web-app alloc
systemd-api.service Systemd unit for the api alloc (with TCP bind marker)
systemd-log-shipper.service Systemd unit for the log-shipper alloc

Walkthrough

Prerequisites

  • Orca installed (orca version works)
  • 2+ Linux nodes reachable over SSH (for multi-node scheduling)
  • Traefik installed on the lead node (watches /etc/traefik/dynamic/)

Step 1: Initialize the cluster

# On the operator laptop
orca init

This creates ~/.orca/ (or /root/.orca with --system), bootstraps the CA, generates the server cert, auto-detects the OS, and registers a localhost node.

Step 2: Join remote nodes

# Join a Proxmox node (v0.9 canonical SSH-push path)
orca node join --type proxmox --host 192.168.1.100 --ssh-user root

# Join a second node
orca node join --type proxmox --host 192.168.1.101 --ssh-key ~/.ssh/orca_ed25519

Step 3: Declare node capacity

The CLI-side scheduler uses capacity declarations for bin-packing:

orca node capacity set --cpu 4000 --memory 8192 --disk 100000 --node 192.168.1.100
orca node capacity set --cpu 4000 --memory 8192 --disk 100000 --node 192.168.1.101

Step 4: Create a namespace

orca ns create prod --parent _defaults

This creates ~/.orca/prod/ with db/, jobs/, alloc/, and ns.md.

Step 5: Submit the stack

orca job run web-app.md
orca job run api.md
orca job run worker.md
orca job run log-shipper.md
orca job run postgres.md

Each orca job run parses the .md jobspec, validates it against the schema, schedules it via the CLI-side bin-packing scheduler, and generates the systemd + Traefik artifacts on the target node via SSH-push.

Step 6: Observe placements

orca job list --watch

# Output:
# ID                                   NAME          STATUS   EXIT
# abc-123...                           web-app       running  0
# def-456...                           api           running  0
# ghi-789...                           worker        complete 0
# jkl-012...                           log-shipper   running  0
# mno-345...                           postgres      running  0

Step 7: Inspect rendered artifacts

After submission, the target nodes have:

/etc/systemd/system/orca-v1-web-app.service       # systemd unit
/etc/systemd/system/orca-v1-api.service           # systemd unit (TCP bind)
/etc/traefik/dynamic/orca-web-app.yaml            # Traefik dynamic config
/etc/traefik/dynamic/orca-api.yaml                # Traefik dynamic config
/run/orca/alloc-web-app-0/port-http.sock          # Unix socket (R-007 default)

See the rendered/ directory in this example for the exact file contents.

Step 8: Verify ingress

Traefik watches /etc/traefik/dynamic/ and atomically reloads when a file changes (write-tmp + rename, gate C-10). The web-app is reachable at https://<cluster-domain>/web-app and the API at https://<cluster-domain>/api.

Health checks (/healthz on each backend) ensure Traefik only routes to healthy instances.

Step 9: Drain and rollback

To drain a service (stop traffic, keep the workload running):

# Orca writes a Traefik config with weight:0 on every backend
# (RenderDrain). Traefik stops sending traffic.

To roll back, re-submit the normal jobspec — Orca writes the non-drained Traefik config and Traefik resumes routing.

Ingress model

See docs/ingress.md for the full Traefik ingress reference. Key points:

  • kind: Service implies a Traefik route (D-175).
  • Default bind is a Unix socket at /run/orca/alloc-<id>/port-<name>.sock (R-007).
  • service.bind: 127.0.0.1 opts in to TCP (loopback only).
  • One Traefik dynamic file per Service at /etc/traefik/dynamic/orca-<name>.yaml.
  • Atomic reload via write-tmp + rename (gate C-10).
  • Drain sets weight: 0 per backend.

Validation

All jobspecs in this directory are validated by a Go test:

go test ./examples/full-stack/ -v -run TestExamplesValidate

This test parses each .md file with jobspec.ParseFile and validates it against schema.ValidatorFor(kind) — ensuring every field used in the examples exists in the current WorkloadSpec struct and passes the per-kind validators (gate C-20).

v0.11 forward

The following are not yet implemented in v0.9 and will land in v0.11:

  • DaemonSet schedule: block: the parser does not yet populate the schedule: frontmatter block (v0.9 parser gap). The log-shipper example uses kind: Service with count: 1 and a node.role constraint as a workaround.
  • Secret resolution: env: { KEY: { from: "secret:..." } } is parsed but not resolved to EnvironmentFile=/LoadCredential= until v0.11-P03.
  • Transactional update execution: the update: block's plan is computed but not executed transactionally until v0.11-P10.
  • Socket activation: real socket unit files land in v0.11-P08.