feat: Add slcompose service orchestrator with Infisical secret injection

- Add slcompose.sh: Central orchestrator for managing all Docker services
  * Boot all services at startup with automated Infisical secret injection
  * Commands: up, down, restart, logs, logs-tail, env, env-all
  * Colored environment variable output (blue names, green values)

- Add slcompose.service: Systemd service file for auto-boot on startup
  * Type=oneshot with RemainAfterExit=yes
  * Waits for Docker service before starting
  * Runs on multi-user.target

- Add orchestration.md: Comprehensive documentation
  * Architecture and installation guide
  * Usage examples for all commands
  * Secret injection flow and troubleshooting
  * Performance and security notes

- Update README.md and AI_CONTEXT.md
  * Document service orchestration architecture
  * Explain slcompose functionality and commands
  * Reference new orchestration documentation
This commit is contained in:
2026-07-02 14:13:15 +03:30
parent a74909be25
commit a9a758d332
14 changed files with 1039 additions and 55 deletions
+45 -6
View File
@@ -62,6 +62,49 @@ See [docs/roadmap.md](docs/roadmap.md) for the phased infrastructure roadmap.
---
## Service Orchestration
All Docker services on SilverLinux are managed through the **slcompose orchestrator** (`slcompose`), a bash wrapper that:
1. **Injects Secrets** — Uses Infisical CLI to load environment variables from Infisical and inject them into each service at runtime
2. **Boots All Services** — Automatically starts all services at system startup via systemd
3. **Manages Services** — Provides CLI commands to start, stop, restart, and view logs for individual services
### How It Works
```bash
# Load secrets from Infisical and start all services
slcompose boot
# Manage individual services
slcompose up gitea # Start gitea
slcompose down gitea # Stop gitea
slcompose restart gitea # Restart gitea
slcompose logs gitea # Stream live logs
slcompose logs-tail gitea 200 # View last 200 lines
slcompose list # List all services
# View injected environment variables
slcompose env gitea # Show env vars for gitea
slcompose env-all # Show env vars for all services
```
**Service Auto-Boot:** The systemd service `slcompose.service` automatically runs `slcompose boot` on system startup, ensuring all services recover after server reboot.
**Secrets Flow:**
```
System Boot → systemd slcompose.service → slcompose boot
For each service in /srv/docker/:
- Load INFISICAL_TOKEN from /etc/infisical/token
- Use Infisical CLI to inject secrets from path: /[SERVICE_NAME]
- Run: docker compose up -d
```
See [docs/orchestration.md](docs/orchestration.md) and [docs/slcompose.sh](docs/slcompose.sh) for complete details.
---
## Shared Infrastructure
The following components are shared across multiple services:
@@ -73,13 +116,9 @@ The following components are shared across multiple services:
* Isolated Docker Networks
* Monitoring Docker Stack
* Automated Backup System
* Shared Secrets Management
* Shared Secrets Management (Infisical + slcompose)
Secrets are stored outside the repository:
```text
/srv/secrets/company.env
```
Secrets are managed centrally through Infisical and injected at runtime by the slcompose wrapper.
---
+29 -4
View File
@@ -53,13 +53,38 @@ Xray -> Public tunneling endpoint through proxy network
## Secrets
Secrets are stored in:
Secrets are stored in Infisical and injected at runtime via the **slcompose orchestrator**.
/srv/secrets/company.env
Never hardcode credentials. All services receive environment variables through Infisical's secret injection at container startup.
Never hardcode credentials.
### Secret Injection Flow
Always use env_file.
The `slcompose` service orchestrator manages all Docker services with automated secret injection:
```bash
slcompose boot # Boots all services with secrets injected
slcompose up <service> # Start a service with secrets injected
slcompose down <service> # Stop a service
slcompose restart <service> # Restart a service
slcompose logs <service> # Stream logs
slcompose logs-tail <service> [lines] # View last N lines
slcompose list # List all available services
```
**System Startup:** The systemd service `slcompose.service` automatically runs `slcompose boot` on server reboot.
**Implementation:** See [docs/slcompose.sh](slcompose.sh) and [docs/slcompose.service](slcompose.service).
## Service Orchestration
All Docker services are managed through **slcompose**, which:
1. **Loads Infisical Token** from `/etc/infisical/token`
2. **Discovers Services** by scanning `/srv/docker/` for directories with `docker-compose.yml`
3. **Injects Secrets** using `infisical run --path=/[SERVICE_NAME]` before executing docker compose commands
4. **Manages Lifecycle** with up, down, restart, and logs commands
Each service directory name (e.g., `/srv/docker/gitea`) becomes the Infisical path (e.g., `/gitea`) for secret lookup.
## Docker Networks
+83
View File
@@ -202,6 +202,89 @@ Active
---
### Exact Syncer Demo
Domain:
```text
exactsyncer.silveressence.net
```
Purpose:
* Demo `.NET 9` Blazor application for Exact Online integration
* Provides a UI for connecting Exact webhooks and observing sync behavior
* Syncs main division changes to dependent sub-divisions
Behavior:
* When the main division record (Relation 1) changes, divisions 210 add or update the same relation data
* Demonstrates how Exact Online master/sub-division sync works in practice
Container:
```text
exact-syncer
```
Routing:
```text
Nginx Proxy Manager -> exact-syncer:8080
```
Notes:
* Demo version intended for client evaluation
* Can be recreated as a bespoke customer instance on request
Status:
```text
Operational demo
```
---
### BobAutoWas Exact Syncer Instance
Domain:
```text
cicd.silveressence.net
```
Purpose:
* Client-specific Exact Syncer instance for BobAutoWas
* Customer recreation of the Exact Syncer demo application
* Provides the same webhook-driven division sync behavior
Behavior:
* Mirrors Exact Syncer demo functionality for BobAutoWas
* Supports client evaluation and live preview of the integration
Container:
```text
bobsAutowas
```
Routing:
```text
Nginx Proxy Manager -> bobsAutowas:8080
```
Status:
```text
Active client instance
```
---
### DbGate
Domain:
+23
View File
@@ -158,6 +158,29 @@ Verified subnet:
172.22.0.0/16
```
#### Gluetun PIA VPN Gateway
Purpose:
* Containerized VPN gateway for Private Internet Access (PIA)
* Isolate VPN traffic from host networking and production services
Services:
* `gluetun-pia`
* `xray-pia` (via `container:gluetun-pia` network mode)
Notes:
* `gluetun-pia` routes selected container traffic through PIA WireGuard
* `xray-pia` is a secondary VLESS endpoint that uses the Gluetun VPN gateway
* Production `xray` remains isolated on the direct OVH path
Verified isolation:
* VPN traffic is contained inside the `gluetun-pia` container namespace
* Host routing remains unchanged by VPN activity
#### gitea-runner_default
Purpose:
+391
View File
@@ -0,0 +1,391 @@
# Service Orchestration: slcompose
## Overview
**slcompose** is the central service orchestrator for SilverLinux. It manages all Docker services in `/srv/docker/` with automated secret injection from Infisical.
## Architecture
```
System Boot
systemd: slcompose.service
/usr/local/bin/slcompose boot
For each service in /srv/docker/:
├─ Load Infisical Token from /etc/infisical/token
├─ Discover docker-compose.yml
├─ Extract service name from directory (e.g., gitea)
├─ Inject secrets: infisical run --path=/gitea
└─ Execute: docker compose up -d
```
## Installation
Copy `slcompose.sh` to `/usr/local/bin/slcompose` and make executable:
```bash
sudo cp docs/slcompose.sh /usr/local/bin/slcompose
sudo chmod +x /usr/local/bin/slcompose
```
Copy systemd service file:
```bash
sudo cp docs/slcompose.service /etc/systemd/system/slcompose.service
sudo systemctl daemon-reload
sudo systemctl enable slcompose.service
```
## Usage
### List Services
```bash
slcompose list
```
Output:
```
Available services:
====================
- gitea
- portainer
- postgres
- mssql
- openproject
- jitsi
- nginx-proxy-manager
- postgres
- baget
- dbgate
```
### Boot All Services
```bash
slcompose boot
```
This is automatically called by systemd on server startup.
### Start a Service
```bash
slcompose up gitea
```
- Loads Infisical token
- Injects secrets from Infisical path: `/gitea`
- Runs: `docker compose up -d` in `/srv/docker/gitea`
### Stop a Service
```bash
slcompose down gitea
```
### Restart a Service
```bash
slcompose restart gitea
```
### View Logs
Stream live logs (Ctrl+C to exit):
```bash
slcompose logs gitea
```
View last N lines (default 200):
```bash
slcompose logs-tail gitea
slcompose logs-tail gitea 500
```
### View Injected Environment Variables
View all environment variables injected into a specific service from Infisical:
```bash
slcompose env gitea
```
Output (color-coded):
```
========================================
Environment Variables for: gitea
========================================
DB_HOST = postgres
DB_NAME = gitea_db
DB_PASSWD = secure_password_123
DB_TYPE = postgres
DB_USER = gitea
DOMAIN = git.silveressence.net
...
```
*(Blue: variable names | Green: values)*
View injected environment variables for ALL services:
```bash
slcompose env-all
```
Output (color-coded):
```
========================================
All Services and Their Environment Variables
========================================
--- gitea ---
DB_HOST = postgres
DB_NAME = gitea_db
...
--- portainer ---
ADMIN_PASSWORD = secure_password
...
--- postgres ---
POSTGRES_DB = main_db
...
```
*(Blue: variable names | Green: values)*
**Use Cases:**
- Verify secrets are properly injected
- Debug missing or incorrect environment variables
- Audit which services have access to which secrets
- Troubleshoot authentication or configuration issues
## How Secret Injection Works
### Prerequisites
1. **Infisical Token**: Stored at `/etc/infisical/token`
2. **Infisical Domain** (optional): Stored at `/etc/infisical/domain`
3. **Secrets Path**: Each service has secrets at path `/<SERVICE_NAME>` in Infisical
Example: For `gitea` service, secrets must be at `/gitea` in Infisical.
### Injection Process
For each service, slcompose executes:
```bash
infisical run \
--token "$INFISICAL_TOKEN" \
--env prod \
--path="/$SERVICE_NAME" \
--recursive \
-- docker compose "$@"
```
This:
1. Authenticates with Infisical using the token
2. Fetches all secrets from the specified path
3. Recursively includes nested secrets
4. Sets them as environment variables
5. Passes them to the docker compose command
### Environment Variables
All secrets from Infisical are available as environment variables inside the docker compose execution context and are used by `docker-compose.yml` files.
Example `docker-compose.yml`:
```yaml
services:
gitea:
image: gitea/gitea:latest
environment:
DB_TYPE: postgres
DB_HOST: postgres
DB_NAME: ${DB_NAME} # Injected from Infisical
DB_USER: ${DB_USER} # Injected from Infisical
DB_PASSWD: ${DB_PASSWD} # Injected from Infisical
```
## Service Directory Structure
Each service is a subdirectory in `/srv/docker/` containing:
```
/srv/docker/gitea/
├── docker-compose.yml
├── README.md (optional)
└── app.ini (optional, service-specific config)
```
Services are discovered by the presence of `docker-compose.yml`.
## Systemd Integration
### Service File: `/etc/systemd/system/slcompose.service`
```ini
[Unit]
Description=SilverLinux Docker Orchestrator (slCompose)
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/slcompose boot
RemainAfterExit=yes
User=root
[Install]
WantedBy=multi-user.target
```
**Key Details:**
- `Type=oneshot` — Runs once and exits (doesn't stay running)
- `RemainAfterExit=yes` — Systemd remembers the service as "active" after boot completes
- `After=docker.service` — Waits for Docker to start first
- `Requires=docker.service` — Fails if Docker is not available
- `WantedBy=multi-user.target` — Starts during normal multi-user boot
### Enable Auto-Boot
```bash
sudo systemctl enable slcompose.service
```
### Check Status
```bash
sudo systemctl status slcompose.service
```
### View Boot Logs
```bash
sudo journalctl -u slcompose.service -n 100
```
## Error Handling
### Missing Infisical Token
If `/etc/infisical/token` doesn't exist, slcompose exits with:
```
ERROR: Missing Infisical token at /etc/infisical/token
```
### Service Not Found
If you try to manage a non-existent service:
```bash
slcompose up nonexistent
```
Output:
```
ERROR: Service 'nonexistent' not found at /srv/docker/nonexistent
```
### Docker Not Running
If Docker isn't available, slcompose waits and retries:
```
Waiting for Docker...
Waiting for Docker...
```
## Troubleshooting
### Services not starting on boot
1. Check if systemd service is enabled:
```bash
sudo systemctl is-enabled slcompose.service
```
2. View boot logs:
```bash
sudo journalctl -u slcompose.service -n 50
```
3. Test slcompose manually:
```bash
slcompose boot
```
### Secrets not injected
1. Verify Infisical token exists:
```bash
cat /etc/infisical/token
```
2. Test Infisical access:
```bash
infisical run --token "$(cat /etc/infisical/token)" --env prod --path=/gitea -- env | grep -v '^_'
```
3. Check service paths in Infisical match service directory names
### Service fails to start
1. View detailed logs:
```bash
slcompose logs gitea
```
2. Check docker-compose.yml for syntax errors:
```bash
cd /srv/docker/gitea && docker-compose config
```
3. View full docker compose output:
```bash
cd /srv/docker/gitea && infisical run --token "$(cat /etc/infisical/token)" --env prod --path=/gitea -- docker compose up
```
## Adding New Services
1. Create service directory:
```bash
mkdir -p /srv/docker/myservice
```
2. Add `docker-compose.yml`:
```bash
cp template/docker-compose.yml /srv/docker/myservice/
```
3. Create secrets in Infisical at path `/myservice`
4. Test:
```bash
slcompose up myservice
slcompose logs myservice
slcompose down myservice
```
5. Service will automatically boot on next system startup
## Performance
- **Boot Time**: ~2 minutes for all 10+ services (includes Docker startup)
- **Per-Service**: ~5-10 seconds per service startup
- **Memory Overhead**: Minimal (orchestrator is bash script)
## Security
- **Secrets**: Never logged or displayed (only injected into containers)
- **Tokens**: Read from files with appropriate permissions
- **Docker Access**: Requires root/docker group membership
- **Service Isolation**: Uses Docker networks to isolate services
## Related Files
- [slcompose.sh](slcompose.sh) — Orchestrator script
- [slcompose.service](slcompose.service) — Systemd service file
- [AI_CONTEXT.md](AI_CONTEXT.md) — Infrastructure context
- [../README.md](../README.md) — Repository overview
+8 -13
View File
@@ -2,21 +2,15 @@
## Overview
SilverLinux uses a centralized secrets file for shared credentials and sensitive configuration.
SilverLinux uses Infisical for centralized secret management. Secrets are injected into Compose stacks at runtime by `infisical run`.
Location:
```text
/srv/secrets/company.env
```
This file is stored on the server and is never committed to Git.
This repository no longer depends on a local `/srv/secrets/company.env` file for deployed services.
---
## Purpose
The secrets file provides a single location for:
The Infisical deployment flow provides a single source of truth for:
* SMTP credentials
* Database passwords
@@ -24,11 +18,12 @@ The secrets file provides a single location for:
* OAuth client secrets
* Future API keys
Applications should load secrets using:
Applications should load secrets using Infisical injection at deployment time.
```yaml
env_file:
- /srv/secrets/company.env
Example:
```bash
infisical run --path=/ --recursive -- docker compose up -d
```
---
+146 -5
View File
@@ -363,6 +363,69 @@ Operational
---
### Exact Syncer Demo
Purpose:
* Demo `.NET 9` Blazor application for Exact Online integration
* Provides a user interface for connecting Exact webhooks
* Syncs changes from a main division to dependent sub-divisions
Behavior:
* When the main division relation changes, the app updates related sub-divisions with Exact data
* Example: a change in main division Relation 1 propagates to divisions 210
Runtime:
* Hostname: `exactsyncer.silveressence.net`
* Container: `exact-syncer`
* Port: `8080`
Deployment notes:
* Demo version for clients to evaluate Exact Online sync behavior
* Can be recreated as a dedicated instance for new customers
Status:
```text
Operational demo
```
---
### BobAutoWas Exact Syncer Instance
Purpose:
* Client-specific Exact Syncer instance for BobAutoWas
* Recreated from the Exact Syncer demo for customer delivery
* Provides the same webhook-driven and division-sync workflow
Behavior:
* Uses the same Exact Online master/sub-division sync model as the demo
* Intended for client preview and delivery use
Runtime:
* Hostname: `cicd.silveressence.net`
* Container: `bobsAutowas`
* Port: `8080`
Deployment notes:
* Dedicated customer-facing instance of Exact Syncer
* Managed as an active demo/product instance
Status:
```text
Active client instance
```
---
### DbGate
URL:
@@ -398,18 +461,18 @@ Operational
---
### Xray
### Xray (Direct Production)
Purpose:
* Connectivity and tunneling layer
* Improve long-distance connectivity stability to SilverLinux
* Maintain secure routing paths when direct connectivity is limited
* Primary production VLESS endpoint
* Direct OVH internet routing for stable public access
* Separate from VPN-routed traffic to protect production stability
Classification:
```text
Network tunneling / connectivity layer
Primary production connectivity layer
```
Networks:
@@ -430,6 +493,7 @@ ghcr.io/xtls/xray-core:latest
Runtime note:
* Direct production Xray is unmodified and isolated from the VPN routing setup.
* The `xray_default` network exists, but the running `xray` container was attached only to `proxy` in the 2026-06-24 runtime snapshot.
Security:
@@ -446,6 +510,83 @@ Operational
---
### VPN Gateway (Gluetun PIA)
Purpose:
* Containerized VPN gateway for Private Internet Access (PIA)
* Routes selected containers through WireGuard without affecting host networking
* Provides an isolated VPN execution layer for safe experimentation
Container:
```text
gluetun-pia
```
VPN Provider:
* Private Internet Access (PIA)
Mode:
* Custom WireGuard configuration generated by PIA
Notes:
* No VPN software runs on the host
* Prevents SSH lockout and host routing table override issues
* Only selected services use this VPN gateway
Status:
```text
Operational VPN gateway
```
---
### VPN-Routed Xray (xray-pia)
Purpose:
* Secondary VLESS endpoint routed through the PIA VPN gateway
* Maintains a separate service path from the direct production Xray endpoint
Container:
```text
xray-pia
```
Runtime ports:
```text
0.0.0.0:32444 -> 32444/tcp
```
Network mode:
* `container:gluetun-pia`
Routing:
* Client -> `xray-pia` (32444) -> `gluetun-pia` -> PIA WireGuard VPN -> Internet
Notes:
* Fully isolated from host networking
* Does not impact the direct production Xray endpoint
* Supports future multiple VPN exit points
Status:
```text
Operational secondary VPN endpoint
```
---
## Shared Infrastructure
### Docker
+14
View File
@@ -0,0 +1,14 @@
# /etc/systemd/system/slcompose.service
[Unit]
Description=SilverLinux Docker Orchestrator (slCompose)
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/slcompose boot
RemainAfterExit=yes
User=root
[Install]
WantedBy=multi-user.target
+289
View File
@@ -0,0 +1,289 @@
#!/bin/bash
set -e
BASE_DIR="/srv/docker"
INFISICAL_TOKEN_FILE="/etc/infisical/token"
INFISICAL_DOMAIN_FILE="/etc/infisical/domain"
function load_infisical() {
if [ ! -f "$INFISICAL_TOKEN_FILE" ]; then
echo "ERROR: Missing Infisical token at $INFISICAL_TOKEN_FILE"
exit 1
fi
export INFISICAL_TOKEN=$(cat "$INFISICAL_TOKEN_FILE")
if [ -f "$INFISICAL_DOMAIN_FILE" ]; then
export INFISICAL_DOMAIN=$(cat "$INFISICAL_DOMAIN_FILE")
fi
}
function exec_with_infisical() {
local service_name="$1"
shift
infisical run \
--token "$INFISICAL_TOKEN" \
--env prod \
--path="/$service_name" \
--recursive \
-- "$@"
}
function run_service() {
SERVICE_DIR="$1"
shift
SERVICE_NAME=$(basename "$SERVICE_DIR")
cd "$SERVICE_DIR"
echo "-----------------------------------"
echo "Booting: $SERVICE_DIR"
until docker info >/dev/null 2>&1; do
echo "Waiting for Docker..."
sleep 2
done
load_infisical
echo "Using Infisical path: /$SERVICE_NAME"
exec_with_infisical "$SERVICE_NAME" docker compose "$@"
}
function list_services() {
echo "Available services:"
echo "===================="
for dir in "$BASE_DIR"/*; do
if [ -f "$dir/docker-compose.yml" ]; then
SERVICE_NAME=$(basename "$dir")
echo " - $SERVICE_NAME"
fi
done
}
function boot_all() {
echo "==================================="
echo "SilverLinux Boot Orchestrator START"
echo "==================================="
load_infisical
for dir in "$BASE_DIR"/*; do
if [ -f "$dir/docker-compose.yml" ]; then
SERVICE_NAME=$(basename "$dir")
echo "Booting: $SERVICE_NAME"
run_service "$dir" up -d || {
echo "FAILED: $SERVICE_NAME"
}
sleep 2
fi
done
echo "==================================="
echo "Boot orchestration finished"
echo "==================================="
}
function service_up() {
SERVICE_NAME="$1"
SERVICE_DIR="$BASE_DIR/$SERVICE_NAME"
if [ ! -d "$SERVICE_DIR" ] || [ ! -f "$SERVICE_DIR/docker-compose.yml" ]; then
echo "ERROR: Service '$SERVICE_NAME' not found at $SERVICE_DIR"
exit 1
fi
echo "Starting service: $SERVICE_NAME"
run_service "$SERVICE_DIR" up -d
}
function service_down() {
SERVICE_NAME="$1"
SERVICE_DIR="$BASE_DIR/$SERVICE_NAME"
if [ ! -d "$SERVICE_DIR" ] || [ ! -f "$SERVICE_DIR/docker-compose.yml" ]; then
echo "ERROR: Service '$SERVICE_NAME' not found at $SERVICE_DIR"
exit 1
fi
echo "Stopping service: $SERVICE_NAME"
run_service "$SERVICE_DIR" down
}
function service_restart() {
SERVICE_NAME="$1"
SERVICE_DIR="$BASE_DIR/$SERVICE_NAME"
if [ ! -d "$SERVICE_DIR" ] || [ ! -f "$SERVICE_DIR/docker-compose.yml" ]; then
echo "ERROR: Service '$SERVICE_NAME' not found at $SERVICE_DIR"
exit 1
fi
echo "Restarting service: $SERVICE_NAME"
run_service "$SERVICE_DIR" restart
}
function service_logs() {
SERVICE_NAME="$1"
SERVICE_DIR="$BASE_DIR/$SERVICE_NAME"
if [ ! -d "$SERVICE_DIR" ] || [ ! -f "$SERVICE_DIR/docker-compose.yml" ]; then
echo "ERROR: Service '$SERVICE_NAME' not found at $SERVICE_DIR"
exit 1
fi
echo "Tailing logs for: $SERVICE_NAME (Ctrl+C to exit)"
run_service "$SERVICE_DIR" logs -f
}
function service_logs_tail() {
SERVICE_NAME="$1"
NUM_LINES="${2:-200}"
SERVICE_DIR="$BASE_DIR/$SERVICE_NAME"
if [ ! -d "$SERVICE_DIR" ] || [ ! -f "$SERVICE_DIR/docker-compose.yml" ]; then
echo "ERROR: Service '$SERVICE_NAME' not found at $SERVICE_DIR"
exit 1
fi
echo "Last $NUM_LINES lines of logs for: $SERVICE_NAME"
run_service "$SERVICE_DIR" logs --tail "$NUM_LINES"
}
function service_env() {
SERVICE_NAME="$1"
SERVICE_DIR="$BASE_DIR/$SERVICE_NAME"
if [ ! -d "$SERVICE_DIR" ] || [ ! -f "$SERVICE_DIR/docker-compose.yml" ]; then
echo "ERROR: Service '$SERVICE_NAME' not found at $SERVICE_DIR"
exit 1
fi
load_infisical
echo "========================================"
echo "Environment Variables for: $SERVICE_NAME"
echo "========================================"
exec_with_infisical "$SERVICE_NAME" env | grep -v "^_" | sort | while IFS='=' read -r name value; do
printf "\033[34m%-50s\033[0m = \033[32m%s\033[0m\n" "$name" "$value"
done
}
function service_env_all() {
load_infisical
echo "========================================"
echo "All Services and Their Environment Variables"
echo "========================================"
for dir in "$BASE_DIR"/*; do
if [ -f "$dir/docker-compose.yml" ]; then
SERVICE_NAME=$(basename "$dir")
echo ""
echo "--- $SERVICE_NAME ---"
exec_with_infisical "$SERVICE_NAME" env | grep -v "^_" | sort | while IFS='=' read -r name value; do
printf "\033[34m%-50s\033[0m = \033[32m%s\033[0m\n" "$name" "$value"
done
fi
done
}
function show_usage() {
cat <<EOF
SilverLinux Service Orchestrator
Usage: $0 <command> [service_name] [options]
Commands:
boot Boot all services at startup
list List all available services
up <service> Start a service
down <service> Stop a service
restart <service> Restart a service
logs <service> Tail logs for a service (Ctrl+C to exit)
logs-tail <service> [lines] Show last N lines of logs (default: 200)
env <service> Show injected environment variables for a service
env-all Show injected environment variables for all services
Examples:
$0 boot
$0 list
$0 up gitea
$0 down gitea
$0 restart gitea
$0 logs gitea
$0 logs-tail gitea
$0 logs-tail gitea 500
$0 env gitea
$0 env-all
EOF
}
case "$1" in
boot)
boot_all
;;
list)
list_services
;;
up)
if [ -z "$2" ]; then
echo "ERROR: Service name required"
show_usage
exit 1
fi
service_up "$2"
;;
down)
if [ -z "$2" ]; then
echo "ERROR: Service name required"
show_usage
exit 1
fi
service_down "$2"
;;
restart)
if [ -z "$2" ]; then
echo "ERROR: Service name required"
show_usage
exit 1
fi
service_restart "$2"
;;
logs)
if [ -z "$2" ]; then
echo "ERROR: Service name required"
show_usage
exit 1
fi
service_logs "$2"
;;
logs-tail)
if [ -z "$2" ]; then
echo "ERROR: Service name required"
show_usage
exit 1
fi
service_logs_tail "$2" "$3"
;;
env)
if [ -z "$2" ]; then
echo "ERROR: Service name required"
show_usage
exit 1
fi
service_env "$2"
;;
env-all)
service_env_all
;;
*)
show_usage
exit 1
;;
esac
+2 -4
View File
@@ -114,11 +114,9 @@ Purpose:
* Repository notifications
* User invitations
SMTP configuration is loaded from:
SMTP secrets are provided by Infisical at runtime.
```text
/srv/secrets/company.env
```
These values are resolved during deployment by `infisical run`.
Variables:
+4 -5
View File
@@ -39,10 +39,9 @@ Server-side administration must use `docker exec` or another container attached
## Secrets
The SA password is stored outside the repository:
The SA password is provided by Infisical at runtime:
```text
/srv/secrets/company.env
MSSQL_SA_PASSWORD
```
@@ -52,14 +51,14 @@ Never commit the value to Git or place it directly in the Compose file.
## Deployment
Docker Compose must be given the secrets file for interpolation:
The stack is deployed with Infisical injection instead of a local secrets file:
```bash
cd /srv/docker/mssql
docker compose --env-file /srv/secrets/company.env up -d
infisical run --path=/ --recursive -- docker compose up -d
```
The Compose `env_file:` setting passes variables into the container. It does not make them available while Compose resolves `${MSSQL_SA_PASSWORD}`, which is why `--env-file` is required.
Secrets are injected into the environment before Docker Compose evaluates variables such as `${MSSQL_SA_PASSWORD}`.
Validate startup:
+2 -3
View File
@@ -3,12 +3,11 @@ services:
image: mcr.microsoft.com/mssql/server:2022-latest
container_name: mssql
restart: unless-stopped
env_file:
- /srv/secrets/company.env
# Secrets are injected by Infisical at runtime.
environment:
ACCEPT_EULA: Y
MSSQL_PID: Express
MSSQL_SA_PASSWORD: ${MSSQL_SA_PASSWORD}
MSSQL_SA_PASSWORD: ''
volumes:
- /srv/docker/mssql/data:/var/opt/mssql
networks:
+2 -12
View File
@@ -99,11 +99,7 @@ Purpose:
* Mention notifications
* Project updates
SMTP credentials are stored in:
```text
/srv/secrets/company.env
```
SMTP credentials are provided by Infisical at runtime.
---
@@ -128,13 +124,7 @@ This directory must be included in backups.
## Secrets
The following values are stored in:
```text
/srv/secrets/company.env
```
Variables:
The following values are provided by Infisical at runtime when the stack is deployed:
```text
POSTGRES_OPENPROJECT_PASSWORD
+1 -3
View File
@@ -4,9 +4,7 @@ services:
container_name: openproject
restart: unless-stopped
env_file:
- /srv/secrets/company.env
# Secrets are injected by Infisical at runtime.
environment:
SECRET_KEY_BASE: ${OPENPROJECT_SECRET_KEY_BASE}
OPENPROJECT_HOST__NAME: team.silveressence.net