feat(portainer): add Portainer-optimized deployment files
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

- Create docker-compose.portainer.yml
  - No env_file directive (Portainer doesn't support it)
  - Port exposed on 0.0.0.0 (Portainer limitation)
  - Simple depends_on syntax
  - All environment variables explicit

- Create docs/PORTAINER-DEPLOYMENT.md
  - Complete Portainer deployment guide
  - Step-by-step instructions
  - Environment variables reference
  - Troubleshooting section
  - Best practices for security and backups

- Update README.md
  - Add Portainer deployment section
  - Reference Portainer deployment guide

Fixes:
- 'open /data/compose/94/.env: no such file or directory'
- 'ignoring IP-address (127.0.0.1:8200:8200/tcp)' warning

Portainer requires different compose syntax than standard docker-compose.
This provides a deployment path optimized for Portainer's stack parser.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-08 17:41:11 -06:00
parent 83dee62f0e
commit 66269fa816
3 changed files with 498 additions and 0 deletions

View File

@@ -208,6 +208,29 @@ docker stack ps mosaic
See [Docker Swarm Deployment Guide](docs/SWARM-DEPLOYMENT.md) and [Quick Reference](docs/SWARM-QUICKREF.md) for complete documentation. See [Docker Swarm Deployment Guide](docs/SWARM-DEPLOYMENT.md) and [Quick Reference](docs/SWARM-QUICKREF.md) for complete documentation.
### Portainer Deployment
**Recommended for GUI-based stack management.**
Portainer provides a web UI for managing Docker containers and stacks. Use the Portainer-optimized compose file:
**File:** `docker-compose.portainer.yml`
**Key differences from standard compose:**
- No `env_file` directive (define variables in Portainer UI)
- Port exposed on all interfaces (Portainer limitation)
- Optimized for Portainer's stack parser
**Quick Steps:**
1. Create `mosaic_internal` overlay network in Portainer
2. Deploy `mosaic-openbao` stack with `docker-compose.portainer.yml`
3. Deploy `mosaic` swarm stack with `docker-compose.swarm.yml`
4. Configure environment variables in Portainer UI
See [Portainer Deployment Guide](docs/PORTAINER-DEPLOYMENT.md) for detailed instructions.
## Project Structure ## Project Structure
``` ```

View File

@@ -0,0 +1,95 @@
# ==============================================
# OpenBao Standalone Deployment - Portainer Version
# ==============================================
#
# This file is optimized for Portainer deployment:
# - No env_file directive (define variables in Portainer's environment editor)
# - Port exposed on all interfaces (Portainer limitation)
# - All environment variables explicitly defined
#
# Usage in Portainer:
# 1. Stacks -> Add Stack
# 2. Name: mosaic-openbao
# 3. Paste this file content
# 4. Add environment variables in "Environment variables" section:
# - IMAGE_TAG=dev
# - OPENBAO_PORT=8200
# 5. Deploy
#
# SECURITY NOTE: Port 8200 will be exposed on 0.0.0.0 (all interfaces)
# Use firewall rules to restrict access if needed.
# ==============================================
services:
# ======================
# OpenBao Secrets Vault
# ======================
openbao:
image: git.mosaicstack.dev/mosaic/stack-openbao:${IMAGE_TAG:-dev}
container_name: mosaic-openbao
command: server -config=/openbao/config/config.hcl
environment:
OPENBAO_ADDR: http://0.0.0.0:8200
ports:
- "${OPENBAO_PORT:-8200}:8200"
volumes:
- openbao_data:/openbao/data
- openbao_logs:/openbao/logs
- openbao_init:/openbao/init
cap_add:
- IPC_LOCK
healthcheck:
test:
- CMD
- wget
- --spider
- --quiet
- http://localhost:8200/v1/sys/health?standbyok=true
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
restart: unless-stopped
networks:
- mosaic_internal
# ======================
# OpenBao Init Sidecar
# ======================
# Auto-initializes and unseals OpenBao on first run
openbao-init:
image: git.mosaicstack.dev/mosaic/stack-openbao:${IMAGE_TAG:-dev}
container_name: mosaic-openbao-init
command: /openbao/init.sh
environment:
OPENBAO_ADDR: http://openbao:8200
volumes:
- openbao_init:/openbao/init
depends_on:
- openbao
restart: "no"
networks:
- mosaic_internal
# ======================
# Volumes
# ======================
volumes:
openbao_data:
name: mosaic-openbao-data
driver: local
openbao_logs:
name: mosaic-openbao-logs
driver: local
openbao_init:
name: mosaic-openbao-init
driver: local
# ======================
# Networks
# ======================
# Connect to the swarm stack's internal network
networks:
mosaic_internal:
external: true
name: mosaic_internal

View File

@@ -0,0 +1,380 @@
# Portainer Deployment Guide
## Overview
This guide covers deploying Mosaic Stack via Portainer. Portainer has specific requirements that differ from standard docker-compose deployments.
## Key Differences
**Portainer Limitations:**
- ❌ Does NOT support `env_file:` directive
- ❌ Does NOT support localhost-only port bindings (`127.0.0.1:port`)
- ❌ Does NOT support health check conditions in `depends_on`
- ✅ DOES support environment variables defined in Portainer UI
- ✅ DOES support external networks
**Solution:** Use `docker-compose.portainer.yml` which is optimized for Portainer.
## Prerequisites
### 1. Create Overlay Network
**In Portainer:**
1. Go to **Networks****Add network**
2. Name: `mosaic_internal`
3. Driver: `overlay`
4. Enable **Attachable**
5. Click **Create network**
**Or via CLI:**
```bash
docker network create --driver=overlay --attachable mosaic_internal
```
### 2. Registry Authentication
If using private registry images from `git.mosaicstack.dev`:
**In Portainer:**
1. Go to **Registries****Add registry**
2. Type: **Custom Registry**
3. Name: `Gitea Mosaic`
4. Registry URL: `git.mosaicstack.dev`
5. Username: Your Gitea username
6. Password: Your Gitea password or access token
7. Click **Add registry**
## Deployment Steps
### Step 1: Deploy OpenBao Stack
**Why First?** OpenBao must be running before the main stack starts, as the API needs to connect to it.
**In Portainer:**
1. Go to **Stacks****Add stack**
2. **Name:** `mosaic-openbao`
3. **Build method:** Web editor
4. **Web editor:** Copy and paste contents of `docker-compose.portainer.yml`
5. **Environment variables:**
```
IMAGE_TAG=dev
OPENBAO_PORT=8200
```
6. Click **Deploy the stack**
**Verify Deployment:**
1. Go to **Stacks** → `mosaic-openbao`
2. Check both containers are running:
- `mosaic-openbao` (should be running)
- `mosaic-openbao-init` (will stop after initialization)
3. View logs:
- Click on `mosaic-openbao-init` → **Logs**
- Look for: `✓ OpenBao initialized successfully`
**Wait 30 seconds** for initialization to complete before proceeding.
### Step 2: Deploy Swarm Stack
**In Portainer:**
1. Go to **Stacks** → **Add stack**
2. **Name:** `mosaic`
3. **Build method:** Repository (recommended) or Web editor
**Option A: Git Repository (Recommended)**
- Repository URL: `https://git.mosaicstack.dev/mosaic/stack`
- Repository reference: `refs/heads/develop`
- Compose path: `docker-compose.swarm.yml`
- Authentication: Enable if repository is private
- Enable **Automatic updates** (optional)
- Update interval: `5m` (checks every 5 minutes)
**Option B: Web Editor**
- Copy and paste contents of `docker-compose.swarm.yml`
4. **Environment variables:**
```
IMAGE_TAG=dev
POSTGRES_PASSWORD=<your-secure-password>
JWT_SECRET=<your-jwt-secret>
BETTER_AUTH_SECRET=<your-auth-secret>
ENCRYPTION_KEY=<your-encryption-key>
OIDC_CLIENT_ID=<your-oidc-client-id>
OIDC_CLIENT_SECRET=<your-oidc-client-secret>
OIDC_ISSUER=https://auth.diversecanvas.com/application/o/mosaic-stack/
OIDC_REDIRECT_URI=https://api.mosaicstack.dev/auth/callback/authentik
OLLAMA_ENDPOINT=http://10.1.1.42:11434
```
5. Click **Deploy the stack**
### Step 3: Verify Deployment
**Check Services:**
1. Go to **Swarm** → **Services**
2. Verify all services show `1/1` replicas:
- `mosaic_postgres`
- `mosaic_valkey`
- `mosaic_api`
- `mosaic_web`
- `mosaic_orchestrator`
**Check Logs:**
1. Go to **Swarm** → **Services** → `mosaic_api`
2. Click on the running task
3. Click **Logs**
4. Look for: `OpenBao connection established` or `Using fallback encryption`
**Access Services:**
- Web UI: http://app.mosaicstack.dev
- API: http://api.mosaicstack.dev/health
- OpenBao: http://<server-ip>:8200/ui
## Environment Variables Reference
### Required for All Deployments
```bash
# Image Configuration
IMAGE_TAG=dev # or 'latest' or specific commit SHA
# Database
POSTGRES_PASSWORD=<secure-password>
DATABASE_URL=postgresql://mosaic:${POSTGRES_PASSWORD}@postgres:5432/mosaic
# Security
JWT_SECRET=<random-32-chars> # openssl rand -base64 32
BETTER_AUTH_SECRET=<random-32-chars>
ENCRYPTION_KEY=<64-char-hex> # openssl rand -hex 32
# External OIDC (Authentik)
OIDC_CLIENT_ID=<from-authentik>
OIDC_CLIENT_SECRET=<from-authentik>
OIDC_ISSUER=https://auth.diversecanvas.com/application/o/mosaic-stack/
OIDC_REDIRECT_URI=https://api.mosaicstack.dev/auth/callback/authentik
# External Ollama
OLLAMA_ENDPOINT=http://10.1.1.42:11434
```
### Optional Variables
```bash
# Ports
OPENBAO_PORT=8200
API_PORT=3001
WEB_PORT=3000
# Cache
VALKEY_MAXMEMORY=256mb
# Logging
LOG_LEVEL=info
DEBUG=false
```
## Updating Stacks
### Update OpenBao
1. Go to **Stacks** → `mosaic-openbao`
2. Click **Editor**
3. Update `IMAGE_TAG` environment variable (or compose file)
4. Click **Update the stack**
5. Enable **Re-pull image**
6. Click **Update**
### Update Swarm Stack
**If using Git Repository:**
- Portainer automatically pulls updates based on update interval
- Or manually: **Stacks** → `mosaic` → **Pull and redeploy**
**If using Web Editor:**
1. Go to **Stacks** → `mosaic`
2. Click **Editor**
3. Update compose file or environment variables
4. Click **Update the stack**
5. Enable **Re-pull images**
6. Click **Update**
## Troubleshooting
### Stack Deployment Failed: "network not found"
**Cause:** `mosaic_internal` network doesn't exist
**Fix:**
```bash
docker network create --driver=overlay --attachable mosaic_internal
```
### OpenBao Unhealthy
**Check logs:**
1. **Containers** → `mosaic-openbao` → **Logs**
2. Look for errors
**Common issues:**
- Port 8200 already in use → Stop conflicting service
- Config file not found → Rebuild image
- Permission denied → Check volume permissions
### API Can't Connect to OpenBao
**Symptoms:**
- API logs show "connection refused" to OpenBao
- API falls back to encryption key
**Check:**
1. **Containers** → `mosaic-openbao` → Verify it's running
2. **Networks** → `mosaic_internal` → Verify both stacks are connected
3. API environment: `OPENBAO_ADDR=http://openbao:8200`
**Test connectivity:**
1. **Containers** → Find API container → **Console**
2. Run: `wget -qO- http://openbao:8200/v1/sys/health`
### Services Not Starting in Swarm Stack
**Check service logs:**
1. **Swarm** → **Services** → Click service
2. Click running/failed task
3. View logs
**Common issues:**
- Missing environment variables
- Image pull failed (check registry authentication)
- Dependency not ready (database, OpenBao)
## Best Practices
### Security
1. **Never expose OpenBao to public internet** without TLS
2. **Use firewall rules** to restrict port 8200 access
3. **Rotate secrets regularly** (JWT_SECRET, API keys)
4. **Use strong passwords** for PostgreSQL
5. **Enable TLS** for production deployments
### Monitoring
**In Portainer:**
1. **Dashboard** → View resource usage
2. **Stacks** → Monitor stack health
3. **Swarm** → **Services** → Monitor replicas
**Set up alerts:**
1. **Notifications** → Configure webhook/email
2. **Events** → Enable service alerts
### Backups
**OpenBao Volumes:**
```bash
# Backup
docker run --rm \
-v mosaic-openbao-data:/data \
-v $(pwd):/backup \
alpine tar czf /backup/openbao-$(date +%Y%m%d).tar.gz -C /data .
# Restore
docker run --rm \
-v mosaic-openbao-data:/data \
-v $(pwd):/backup \
alpine sh -c 'rm -rf /data/* && tar xzf /backup/openbao-20260208.tar.gz -C /data'
```
**PostgreSQL:**
```bash
# Backup
docker exec mosaic_postgres pg_dump -U mosaic mosaic > backup.sql
# Restore
docker exec -i mosaic_postgres psql -U mosaic mosaic < backup.sql
```
## Advanced Configuration
### Using External Services
To use external PostgreSQL, Valkey, or Vault:
1. Edit `docker-compose.swarm.yml` in Portainer
2. Comment out the service you want to replace
3. Update environment variables to point to external service
4. Update the stack
**Example - External PostgreSQL:**
```yaml
# Comment out postgres service
# postgres:
# ...
# Update API environment
services:
api:
environment:
DATABASE_URL: postgresql://user:pass@external-db.example.com:5432/mosaic
```
### Custom Domains
Update environment variables:
```bash
NEXT_PUBLIC_APP_URL=https://mosaic.example.com
NEXT_PUBLIC_API_URL=https://api.example.com
OIDC_REDIRECT_URI=https://api.example.com/auth/callback/authentik
```
### Resource Limits
Add to services in Portainer editor:
```yaml
services:
api:
deploy:
resources:
limits:
cpus: "1"
memory: 1G
reservations:
cpus: "0.5"
memory: 512M
```
## See Also
- [Swarm Deployment Guide](SWARM-DEPLOYMENT.md) - CLI-based deployment
- [OpenBao Deployment Guide](OPENBAO-DEPLOYMENT.md) - OpenBao options
- [Configuration Guide](CONFIGURATION.md) - All environment variables
- [Docker Compose Guide](../docker/DOCKER-COMPOSE-GUIDE.md) - Standalone deployment