Unified Quickstart Stack
Docker Compose Multi-Service Ecosystem Unified SSO Production Ready
This guide walks through deploying the complete CloudRader ecosystem stack using Docker Compose. The stack orchestrates central single sign-on (SSO), shared relational database infrastructure, and modular application services (Reservium for room & space scheduling, and Inventarium for equipment tracking).
Deploying Reservium Standalone?
If you only need room & calendar booking without running other CloudRader services or the central ecosystem stack, refer directly to the Reservium Standalone Hosting Guide .
This guide focuses on the organization-wide deployment where multiple CloudRader services run together, sharing central authentication and database infrastructure.
Stack Architecture & Modularity
The CloudRader ecosystem follows a decoupled, modular architecture:
-
Shared Database Infrastructure
A single PostgreSQL 16 instance maintains isolated databases (
keycloak,reservium,inventarium), ensuring clean data separation with minimal resource overhead. -
Central Single Sign-On (SSO)
An OpenID Connect (OIDC) identity provider (Keycloak reference implementation) authenticates users once for all ecosystem services.
-
Reservium (Bookings & Spaces)
FastAPI backend and React UI for meeting room bookings, desk reservations, calendar views, and manager approvals.
-
Inventarium (Asset Management)
Kotlin / Spring Boot backend service for equipment custody, check-in/check-out tracking, and hardware lifecycle management.
Step 1. Project Directory & Configuration
Create a dedicated directory for your deployment:
mkdir -p /opt/cloudrader && cd /opt/cloudrader
1. Environment Variables (.env)
Create an .env file containing your deployment configuration. Replace the placeholder passwords with strong secrets:
# ==============================================================================
# CloudRader Ecosystem Configuration
# ==============================================================================
# Shared PostgreSQL Database
POSTGRES_USER=cloudrader
POSTGRES_PASSWORD=generate_a_secure_database_password_here
POSTGRES_DB=cloudrader
# Central Identity Provider (Keycloak Reference Implementation)
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=generate_a_secure_admin_password_here
KEYCLOAK_DB=keycloak
# Reservium Service Secrets
RESERVIUM_CLIENT_SECRET=generate_a_secure_reservium_secret_here
# Inventarium Service Secrets
INVENTARIUM_CLIENT_SECRET=generate_a_secure_inventarium_secret_here
Generating Secure Passwords
You can quickly generate cryptographically strong passwords with openssl:
openssl rand -base64 24
2. Database Initialization Script (init-db.sh)
Because PostgreSQL initializes with a single default database, create a lightweight initialization script to automatically create isolated databases for each service on first boot:
cat << 'EOF' > init-db.sh
#!/bin/sh
set -e
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
CREATE DATABASE keycloak;
CREATE DATABASE reservium;
CREATE DATABASE inventarium;
EOSQL
EOF
chmod +x init-db.sh
Step 2. Docker Compose Configuration
Create compose.yaml (or docker-compose.yml) in the same directory:
services:
# ============================================================================
# Core Infrastructure Tier
# ============================================================================
postgres:
image: postgres:16-alpine
container_name: cloudrader-postgres
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init-db.sh:/docker-entrypoint-initdb.d/01-init-databases.sh:ro
networks:
- cloudrader-net
keycloak:
image: quay.io/keycloak/keycloak:26.4
container_name: cloudrader-keycloak
restart: unless-stopped
command: start-dev
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/${KEYCLOAK_DB}
KC_DB_USERNAME: ${POSTGRES_USER}
KC_DB_PASSWORD: ${POSTGRES_PASSWORD}
KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN}
KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
ports:
- "8443:8080"
depends_on:
postgres:
condition: service_healthy
networks:
- cloudrader-net
# ============================================================================
# Application Services: Reservium (Room & Space Booking)
# ============================================================================
reservium-api:
image: ghcr.io/cloudrader/reservium-api:latest
container_name: cloudrader-reservium-api
restart: unless-stopped
environment:
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/reservium
KEYCLOAK__SERVER_URL: http://keycloak:8080
KEYCLOAK__REALM: cloudrader
KEYCLOAK__CLIENT_ID: reservium-app
KEYCLOAK__CLIENT_SECRET: ${RESERVIUM_CLIENT_SECRET}
ports:
- "8000:8000"
depends_on:
postgres:
condition: service_healthy
keycloak:
condition: service_started
networks:
- cloudrader-net
reservium-ui:
image: ghcr.io/cloudrader/reservium-ui:latest
container_name: cloudrader-reservium-ui
restart: unless-stopped
ports:
- "3000:80"
depends_on:
- reservium-api
networks:
- cloudrader-net
# ============================================================================
# Application Services: Inventarium (Asset & Equipment Tracking)
# ============================================================================
inventarium-api:
image: ghcr.io/cloudrader/inventarium-api:latest
container_name: cloudrader-inventarium-api
restart: unless-stopped
environment:
POSTGRES_HOST: postgres
POSTGRES_PORT: 5432
POSTGRES_DB: inventarium
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
OIDC_ISSUER_URI: http://keycloak:8080/realms/cloudrader
OIDC_CLIENT_ID: inventarium-app
OIDC_CLIENT_SECRET: ${INVENTARIUM_CLIENT_SECRET}
ports:
- "8010:8080"
depends_on:
postgres:
condition: service_healthy
keycloak:
condition: service_started
networks:
- cloudrader-net
volumes:
postgres_data:
networks:
cloudrader-net:
driver: bridge
Step 3. Launching & Operating the Stack
Start all containers in detached mode:
docker compose up -d
Selective / Modular Deployments
Because CloudRader services are autonomous, you can choose to run only the services your organization currently needs. For instance, to start only the core infrastructure and Reservium:
docker compose up -d postgres keycloak reservium-api reservium-ui
Check the health and runtime status of running containers:
docker compose ps
To stream live logs across all services:
docker compose logs -f
Step 4. Accessing Your Services
Once container startup finishes, access your ecosystem endpoints:
| Service | Local Endpoint | Function & Default Access |
|---|---|---|
| Central Identity (Keycloak) | http://localhost:8443 | Organization SSO, user accounts, and OIDC client secrets |
| Reservium Web Interface | http://localhost:3000 | Room booking calendar for team members and space managers |
| Reservium API Docs (Swagger) | http://localhost:8000/docs | Interactive OpenAPI 3.0 documentation for reservations |
| Inventarium API Docs (Swagger) | http://localhost:8010/swagger-ui.html | OpenAPI documentation for asset & equipment tracking |
Production Security Checklist
Recommended Hardening for Production
- Keep
.envPrivate: Never commit.envto version control. - Change Default Passwords: Ensure all database and admin credentials use strong random secrets.
- Use a Reverse Proxy: Do not expose container ports directly to the internet. Terminate HTTPS and route subdomains with Caddy or Traefik.
- Isolated OIDC Clients: Configure separate client credentials in your Identity Provider for each application service (e.g.,
reservium-appandinventarium-app).
Next Steps
- Set up SSO realms, users, and client secrets in the Identity Provider Guide.
- Terminate HTTPS and configure subdomain routing with Reverse Proxy & TLS.
- Automate nightly database snapshots and volume backups with Backup & Maintenance.
- For standalone Reservium setup, see the Reservium Dedicated Documentation .