8.9 KiB
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:
- Go to Networks → Add network
- Name:
mosaic_internal - Driver:
overlay - Enable Attachable
- Click Create network
Or via CLI:
docker network create --driver=overlay --attachable mosaic_internal
2. Registry Authentication
If using private registry images from git.mosaicstack.dev:
In Portainer:
- Go to Registries → Add registry
- Type: Custom Registry
- Name:
Gitea Mosaic - Registry URL:
git.mosaicstack.dev - Username: Your Gitea username
- Password: Your Gitea password or access token
- 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:
- Go to Stacks → Add stack
- Name:
mosaic-openbao - Build method: Web editor
- Web editor: Copy and paste contents of
docker-compose.portainer.yml - Environment variables:
IMAGE_TAG=dev OPENBAO_PORT=8200 - Click Deploy the stack
Verify Deployment:
- Go to Stacks →
mosaic-openbao - Check both containers are running:
mosaic-openbao(should be running)mosaic-openbao-init(will stop after initialization)
- View logs:
- Click on
mosaic-openbao-init→ Logs - Look for:
✓ OpenBao initialized successfully
- Click on
Wait 30 seconds for initialization to complete before proceeding.
Step 2: Deploy Swarm Stack
In Portainer:
- Go to Stacks → Add stack
- Name:
mosaic - 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
-
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/oauth2/callback/authentik OLLAMA_ENDPOINT=http://10.1.1.42:11434 -
Click Deploy the stack
Step 3: Verify Deployment
Check Services:
- Go to Swarm → Services
- Verify all services show
1/1replicas:mosaic_postgresmosaic_valkeymosaic_apimosaic_webmosaic_orchestrator
Check Logs:
- Go to Swarm → Services →
mosaic_api - Click on the running task
- Click Logs
- Look for:
OpenBao connection establishedorUsing fallback encryption
Access Services:
- Web UI: http://app.mosaicstack.dev
- API: http://api.mosaicstack.dev/health
- OpenBao: http://:8200/ui
Environment Variables Reference
Required for All Deployments
# 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/oauth2/callback/authentik
# External Ollama
OLLAMA_ENDPOINT=http://10.1.1.42:11434
Optional Variables
# Ports
OPENBAO_PORT=8200
API_PORT=3001
WEB_PORT=3000
# Cache
VALKEY_MAXMEMORY=256mb
# Logging
LOG_LEVEL=info
DEBUG=false
Updating Stacks
Update OpenBao
- Go to Stacks →
mosaic-openbao - Click Editor
- Update
IMAGE_TAGenvironment variable (or compose file) - Click Update the stack
- Enable Re-pull image
- 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:
- Go to Stacks →
mosaic - Click Editor
- Update compose file or environment variables
- Click Update the stack
- Enable Re-pull images
- Click Update
Troubleshooting
Stack Deployment Failed: "network not found"
Cause: mosaic_internal network doesn't exist
Fix:
docker network create --driver=overlay --attachable mosaic_internal
OpenBao Unhealthy
Check logs:
- Containers →
mosaic-openbao→ Logs - 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:
- Containers →
mosaic-openbao→ Verify it's running - Networks →
mosaic_internal→ Verify both stacks are connected - API environment:
OPENBAO_ADDR=http://openbao:8200
Test connectivity:
- Containers → Find API container → Console
- Run:
wget -qO- http://openbao:8200/v1/sys/health
Services Not Starting in Swarm Stack
Check service logs:
- Swarm → Services → Click service
- Click running/failed task
- View logs
Common issues:
- Missing environment variables
- Image pull failed (check registry authentication)
- Dependency not ready (database, OpenBao)
Best Practices
Security
- Never expose OpenBao to public internet without TLS
- Use firewall rules to restrict port 8200 access
- Rotate secrets regularly (JWT_SECRET, API keys)
- Use strong passwords for PostgreSQL
- Enable TLS for production deployments
Monitoring
In Portainer:
- Dashboard → View resource usage
- Stacks → Monitor stack health
- Swarm → Services → Monitor replicas
Set up alerts:
- Notifications → Configure webhook/email
- Events → Enable service alerts
Backups
OpenBao Volumes:
# 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:
# 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:
- Edit
docker-compose.swarm.ymlin Portainer - Comment out the service you want to replace
- Update environment variables to point to external service
- Update the stack
Example - External PostgreSQL:
# 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:
NEXT_PUBLIC_APP_URL=https://mosaic.example.com
NEXT_PUBLIC_API_URL=https://api.example.com
OIDC_REDIRECT_URI=https://api.example.com/auth/oauth2/callback/authentik
Resource Limits
Add to services in Portainer editor:
services:
api:
deploy:
resources:
limits:
cpus: "1"
memory: 1G
reservations:
cpus: "0.5"
memory: 512M
See Also
- Swarm Deployment Guide - CLI-based deployment
- OpenBao Deployment Guide - OpenBao options
- Configuration Guide - All environment variables
- Docker Compose Guide - Standalone deployment