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:
@@ -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
|
## Shared Infrastructure
|
||||||
|
|
||||||
The following components are shared across multiple services:
|
The following components are shared across multiple services:
|
||||||
@@ -73,13 +116,9 @@ The following components are shared across multiple services:
|
|||||||
* Isolated Docker Networks
|
* Isolated Docker Networks
|
||||||
* Monitoring Docker Stack
|
* Monitoring Docker Stack
|
||||||
* Automated Backup System
|
* Automated Backup System
|
||||||
* Shared Secrets Management
|
* Shared Secrets Management (Infisical + slcompose)
|
||||||
|
|
||||||
Secrets are stored outside the repository:
|
Secrets are managed centrally through Infisical and injected at runtime by the slcompose wrapper.
|
||||||
|
|
||||||
```text
|
|
||||||
/srv/secrets/company.env
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+29
-4
@@ -53,13 +53,38 @@ Xray -> Public tunneling endpoint through proxy network
|
|||||||
|
|
||||||
## Secrets
|
## 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
|
## Docker Networks
|
||||||
|
|
||||||
|
|||||||
@@ -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 2–10 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
|
### DbGate
|
||||||
|
|
||||||
Domain:
|
Domain:
|
||||||
|
|||||||
@@ -158,6 +158,29 @@ Verified subnet:
|
|||||||
172.22.0.0/16
|
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
|
#### gitea-runner_default
|
||||||
|
|
||||||
Purpose:
|
Purpose:
|
||||||
|
|||||||
@@ -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
@@ -2,21 +2,15 @@
|
|||||||
|
|
||||||
## Overview
|
## 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:
|
This repository no longer depends on a local `/srv/secrets/company.env` file for deployed services.
|
||||||
|
|
||||||
```text
|
|
||||||
/srv/secrets/company.env
|
|
||||||
```
|
|
||||||
|
|
||||||
This file is stored on the server and is never committed to Git.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
The secrets file provides a single location for:
|
The Infisical deployment flow provides a single source of truth for:
|
||||||
|
|
||||||
* SMTP credentials
|
* SMTP credentials
|
||||||
* Database passwords
|
* Database passwords
|
||||||
@@ -24,11 +18,12 @@ The secrets file provides a single location for:
|
|||||||
* OAuth client secrets
|
* OAuth client secrets
|
||||||
* Future API keys
|
* Future API keys
|
||||||
|
|
||||||
Applications should load secrets using:
|
Applications should load secrets using Infisical injection at deployment time.
|
||||||
|
|
||||||
```yaml
|
Example:
|
||||||
env_file:
|
|
||||||
- /srv/secrets/company.env
|
```bash
|
||||||
|
infisical run --path=/ --recursive -- docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
+146
-5
@@ -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 2–10
|
||||||
|
|
||||||
|
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
|
### DbGate
|
||||||
|
|
||||||
URL:
|
URL:
|
||||||
@@ -398,18 +461,18 @@ Operational
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Xray
|
### Xray (Direct Production)
|
||||||
|
|
||||||
Purpose:
|
Purpose:
|
||||||
|
|
||||||
* Connectivity and tunneling layer
|
* Primary production VLESS endpoint
|
||||||
* Improve long-distance connectivity stability to SilverLinux
|
* Direct OVH internet routing for stable public access
|
||||||
* Maintain secure routing paths when direct connectivity is limited
|
* Separate from VPN-routed traffic to protect production stability
|
||||||
|
|
||||||
Classification:
|
Classification:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Network tunneling / connectivity layer
|
Primary production connectivity layer
|
||||||
```
|
```
|
||||||
|
|
||||||
Networks:
|
Networks:
|
||||||
@@ -430,6 +493,7 @@ ghcr.io/xtls/xray-core:latest
|
|||||||
|
|
||||||
Runtime note:
|
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.
|
* The `xray_default` network exists, but the running `xray` container was attached only to `proxy` in the 2026-06-24 runtime snapshot.
|
||||||
|
|
||||||
Security:
|
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
|
## Shared Infrastructure
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
@@ -114,11 +114,9 @@ Purpose:
|
|||||||
* Repository notifications
|
* Repository notifications
|
||||||
* User invitations
|
* User invitations
|
||||||
|
|
||||||
SMTP configuration is loaded from:
|
SMTP secrets are provided by Infisical at runtime.
|
||||||
|
|
||||||
```text
|
These values are resolved during deployment by `infisical run`.
|
||||||
/srv/secrets/company.env
|
|
||||||
```
|
|
||||||
|
|
||||||
Variables:
|
Variables:
|
||||||
|
|
||||||
|
|||||||
+4
-5
@@ -39,10 +39,9 @@ Server-side administration must use `docker exec` or another container attached
|
|||||||
|
|
||||||
## Secrets
|
## Secrets
|
||||||
|
|
||||||
The SA password is stored outside the repository:
|
The SA password is provided by Infisical at runtime:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
/srv/secrets/company.env
|
|
||||||
MSSQL_SA_PASSWORD
|
MSSQL_SA_PASSWORD
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -52,14 +51,14 @@ Never commit the value to Git or place it directly in the Compose file.
|
|||||||
|
|
||||||
## Deployment
|
## 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
|
```bash
|
||||||
cd /srv/docker/mssql
|
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:
|
Validate startup:
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,11 @@ services:
|
|||||||
image: mcr.microsoft.com/mssql/server:2022-latest
|
image: mcr.microsoft.com/mssql/server:2022-latest
|
||||||
container_name: mssql
|
container_name: mssql
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
env_file:
|
# Secrets are injected by Infisical at runtime.
|
||||||
- /srv/secrets/company.env
|
|
||||||
environment:
|
environment:
|
||||||
ACCEPT_EULA: Y
|
ACCEPT_EULA: Y
|
||||||
MSSQL_PID: Express
|
MSSQL_PID: Express
|
||||||
MSSQL_SA_PASSWORD: ${MSSQL_SA_PASSWORD}
|
MSSQL_SA_PASSWORD: ''
|
||||||
volumes:
|
volumes:
|
||||||
- /srv/docker/mssql/data:/var/opt/mssql
|
- /srv/docker/mssql/data:/var/opt/mssql
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
+2
-12
@@ -99,11 +99,7 @@ Purpose:
|
|||||||
* Mention notifications
|
* Mention notifications
|
||||||
* Project updates
|
* Project updates
|
||||||
|
|
||||||
SMTP credentials are stored in:
|
SMTP credentials are provided by Infisical at runtime.
|
||||||
|
|
||||||
```text
|
|
||||||
/srv/secrets/company.env
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -128,13 +124,7 @@ This directory must be included in backups.
|
|||||||
|
|
||||||
## Secrets
|
## Secrets
|
||||||
|
|
||||||
The following values are stored in:
|
The following values are provided by Infisical at runtime when the stack is deployed:
|
||||||
|
|
||||||
```text
|
|
||||||
/srv/secrets/company.env
|
|
||||||
```
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
POSTGRES_OPENPROJECT_PASSWORD
|
POSTGRES_OPENPROJECT_PASSWORD
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ services:
|
|||||||
container_name: openproject
|
container_name: openproject
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
env_file:
|
# Secrets are injected by Infisical at runtime.
|
||||||
- /srv/secrets/company.env
|
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
SECRET_KEY_BASE: ${OPENPROJECT_SECRET_KEY_BASE}
|
SECRET_KEY_BASE: ${OPENPROJECT_SECRET_KEY_BASE}
|
||||||
OPENPROJECT_HOST__NAME: team.silveressence.net
|
OPENPROJECT_HOST__NAME: team.silveressence.net
|
||||||
|
|||||||
Reference in New Issue
Block a user