Some checks failed
Build and Push Docker Image to Gitea / build-and-push (push) Failing after 1m22s
- Modern web interface for Supervisor process management - Built with Next.js 16 (App Router) and Tailwind CSS 4 - Full XML-RPC client implementation for Supervisor API - Real-time process monitoring with auto-refresh - Process control: start, stop, restart operations - Modern dashboard with system status and statistics - Dark/light theme with OKLCH color system - Docker multi-stage build with runtime env var configuration - Gitea CI/CD workflow for automated builds - Comprehensive documentation (README, IMPLEMENTATION, DEPLOYMENT) Features: - Backend proxy pattern for secure API communication - React Query for state management and caching - TypeScript strict mode with Zod validation - Responsive design with mobile support - Health check endpoint for monitoring - Non-root user security in Docker Environment Variables: - SUPERVISOR_HOST, SUPERVISOR_PORT - SUPERVISOR_USERNAME, SUPERVISOR_PASSWORD (optional) - Configurable at build-time and runtime 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
551 lines
12 KiB
Markdown
551 lines
12 KiB
Markdown
# Deployment Guide - Supervisor UI
|
|
|
|
## Environment Variables
|
|
|
|
The Supervisor UI supports flexible configuration through environment variables that can be set at both build-time and runtime.
|
|
|
|
### Available Environment Variables
|
|
|
|
| Variable | Description | Default | Required |
|
|
|----------|-------------|---------|----------|
|
|
| `SUPERVISOR_HOST` | Supervisor API host/IP address | `localhost` | Yes |
|
|
| `SUPERVISOR_PORT` | Supervisor API port | `9001` | Yes |
|
|
| `SUPERVISOR_USERNAME` | Basic auth username (if enabled) | - | No |
|
|
| `SUPERVISOR_PASSWORD` | Basic auth password (if enabled) | - | No |
|
|
| `NODE_ENV` | Node environment | `production` | No |
|
|
| `PORT` | Application port | `3000` | No |
|
|
|
|
### Configuration Priority
|
|
|
|
Environment variables can be set at different stages, with runtime taking precedence:
|
|
|
|
1. **Runtime** (highest priority) - Docker run `-e` flags or docker-compose environment
|
|
2. **Build-time** - Docker build `--build-arg` flags
|
|
3. **Dockerfile defaults** (lowest priority)
|
|
|
|
## Docker Build Arguments
|
|
|
|
You can customize the build with build arguments:
|
|
|
|
```bash
|
|
docker build -t supervisor-ui \
|
|
--build-arg SUPERVISOR_HOST=supervisor.example.com \
|
|
--build-arg SUPERVISOR_PORT=9001 \
|
|
--build-arg SUPERVISOR_USERNAME=admin \
|
|
--build-arg SUPERVISOR_PASSWORD=secret \
|
|
.
|
|
```
|
|
|
|
**Note**: Build arguments are embedded in the image layer. For sensitive credentials, prefer runtime environment variables.
|
|
|
|
## Docker Runtime Configuration
|
|
|
|
### Option 1: Environment Variables via Docker Run
|
|
|
|
```bash
|
|
docker run -d \
|
|
--name supervisor-ui \
|
|
-p 3000:3000 \
|
|
-e SUPERVISOR_HOST=supervisor.example.com \
|
|
-e SUPERVISOR_PORT=9001 \
|
|
-e SUPERVISOR_USERNAME=admin \
|
|
-e SUPERVISOR_PASSWORD=secret \
|
|
dev.pivoine.art/valknar/supervisor-ui:latest
|
|
```
|
|
|
|
### Option 2: Environment File
|
|
|
|
Create an `.env` file:
|
|
|
|
```env
|
|
SUPERVISOR_HOST=supervisor.example.com
|
|
SUPERVISOR_PORT=9001
|
|
SUPERVISOR_USERNAME=admin
|
|
SUPERVISOR_PASSWORD=secret
|
|
```
|
|
|
|
Run with env file:
|
|
|
|
```bash
|
|
docker run -d \
|
|
--name supervisor-ui \
|
|
-p 3000:3000 \
|
|
--env-file .env \
|
|
dev.pivoine.art/valknar/supervisor-ui:latest
|
|
```
|
|
|
|
### Option 3: Docker Compose
|
|
|
|
Create a `.env` file in the same directory as `docker-compose.yml`:
|
|
|
|
```env
|
|
SUPERVISOR_HOST=supervisor.example.com
|
|
SUPERVISOR_PORT=9001
|
|
SUPERVISOR_USERNAME=admin
|
|
SUPERVISOR_PASSWORD=secret
|
|
```
|
|
|
|
The `docker-compose.yml` automatically picks up these variables:
|
|
|
|
```bash
|
|
docker-compose up -d
|
|
```
|
|
|
|
### Option 4: Docker Compose with Inline Environment
|
|
|
|
Edit `docker-compose.yml`:
|
|
|
|
```yaml
|
|
services:
|
|
supervisor-ui:
|
|
image: dev.pivoine.art/valknar/supervisor-ui:latest
|
|
environment:
|
|
- SUPERVISOR_HOST=supervisor.example.com
|
|
- SUPERVISOR_PORT=9001
|
|
- SUPERVISOR_USERNAME=admin
|
|
- SUPERVISOR_PASSWORD=secret
|
|
```
|
|
|
|
## CI/CD - Gitea Workflows
|
|
|
|
### Automatic Builds
|
|
|
|
The project includes a Gitea Actions workflow that automatically builds and pushes Docker images to the Gitea registry.
|
|
|
|
**Workflow file**: `.gitea/workflows/docker-build-push.yml`
|
|
|
|
### Trigger Events
|
|
|
|
Images are built and pushed on:
|
|
|
|
1. **Push to main branch** → Tagged as `latest` and `main-<sha>`
|
|
2. **Push to develop branch** → Tagged as `develop` and `develop-<sha>`
|
|
3. **Git tags** (v*.*.* pattern) → Tagged as version (e.g., `v1.0.0`, `1.0`, `1`)
|
|
4. **Pull requests** → Built but not pushed (validation only)
|
|
5. **Manual workflow dispatch** → Custom tag specified by user
|
|
|
|
### Workflow Secrets
|
|
|
|
The workflow requires one secret to be configured in your Gitea repository:
|
|
|
|
- `REGISTRY_TOKEN` - Gitea personal access token with registry push permissions
|
|
|
|
#### Creating the Registry Token
|
|
|
|
1. Go to Gitea Settings → Applications → Generate New Token
|
|
2. Name: `GitHub Actions Registry`
|
|
3. Select scope: `write:package`
|
|
4. Copy the generated token
|
|
5. Go to Repository Settings → Secrets → Add Secret
|
|
6. Name: `REGISTRY_TOKEN`
|
|
7. Value: Paste the token
|
|
|
|
### Registry Authentication
|
|
|
|
To pull images from the Gitea registry:
|
|
|
|
```bash
|
|
# Login to registry
|
|
docker login dev.pivoine.art
|
|
|
|
# Pull image
|
|
docker pull dev.pivoine.art/valknar/supervisor-ui:latest
|
|
```
|
|
|
|
### Image Tags
|
|
|
|
After pushing to main, the following tags are available:
|
|
|
|
```bash
|
|
# Latest stable
|
|
dev.pivoine.art/valknar/supervisor-ui:latest
|
|
|
|
# Specific commit
|
|
dev.pivoine.art/valknar/supervisor-ui:main-abc1234
|
|
|
|
# Version tags (from git tags)
|
|
dev.pivoine.art/valknar/supervisor-ui:v1.0.0
|
|
dev.pivoine.art/valknar/supervisor-ui:1.0
|
|
dev.pivoine.art/valknar/supervisor-ui:1
|
|
|
|
# Development branch
|
|
dev.pivoine.art/valknar/supervisor-ui:develop
|
|
```
|
|
|
|
## Production Deployment Scenarios
|
|
|
|
### Scenario 1: Supervisor on Same Host
|
|
|
|
Deploy UI on the same host as Supervisor:
|
|
|
|
```bash
|
|
docker run -d \
|
|
--name supervisor-ui \
|
|
--network host \
|
|
-e SUPERVISOR_HOST=localhost \
|
|
-e SUPERVISOR_PORT=9001 \
|
|
dev.pivoine.art/valknar/supervisor-ui:latest
|
|
```
|
|
|
|
Using `--network host` allows the container to access localhost services.
|
|
|
|
### Scenario 2: Supervisor on Different Host
|
|
|
|
Deploy UI on a different host:
|
|
|
|
```bash
|
|
docker run -d \
|
|
--name supervisor-ui \
|
|
-p 3000:3000 \
|
|
-e SUPERVISOR_HOST=192.168.1.100 \
|
|
-e SUPERVISOR_PORT=9001 \
|
|
dev.pivoine.art/valknar/supervisor-ui:latest
|
|
```
|
|
|
|
Ensure the Supervisor inet_http_server is accessible from the UI host (not bound to 127.0.0.1).
|
|
|
|
### Scenario 3: Multiple Supervisor Instances (Future)
|
|
|
|
While the current version connects to a single instance, you can run multiple UI containers:
|
|
|
|
```bash
|
|
# Production Supervisor UI
|
|
docker run -d \
|
|
--name supervisor-ui-prod \
|
|
-p 3000:3000 \
|
|
-e SUPERVISOR_HOST=prod.supervisor.local \
|
|
dev.pivoine.art/valknar/supervisor-ui:latest
|
|
|
|
# Staging Supervisor UI
|
|
docker run -d \
|
|
--name supervisor-ui-staging \
|
|
-p 3001:3000 \
|
|
-e SUPERVISOR_HOST=staging.supervisor.local \
|
|
dev.pivoine.art/valknar/supervisor-ui:latest
|
|
```
|
|
|
|
### Scenario 4: Behind Reverse Proxy (nginx/Traefik)
|
|
|
|
Example nginx configuration:
|
|
|
|
```nginx
|
|
server {
|
|
listen 80;
|
|
server_name supervisor.example.com;
|
|
|
|
location / {
|
|
proxy_pass http://localhost:3000;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
}
|
|
}
|
|
```
|
|
|
|
Example Traefik docker-compose labels:
|
|
|
|
```yaml
|
|
services:
|
|
supervisor-ui:
|
|
image: dev.pivoine.art/valknar/supervisor-ui:latest
|
|
environment:
|
|
- SUPERVISOR_HOST=supervisor.local
|
|
- SUPERVISOR_PORT=9001
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.http.routers.supervisor-ui.rule=Host(`supervisor.example.com`)"
|
|
- "traefik.http.routers.supervisor-ui.entrypoints=websecure"
|
|
- "traefik.http.routers.supervisor-ui.tls.certresolver=letsencrypt"
|
|
- "traefik.http.services.supervisor-ui.loadbalancer.server.port=3000"
|
|
```
|
|
|
|
### Scenario 5: Docker Compose Stack
|
|
|
|
Complete production-ready stack with reverse proxy:
|
|
|
|
```yaml
|
|
version: '3.8'
|
|
|
|
services:
|
|
supervisor-ui:
|
|
image: dev.pivoine.art/valknar/supervisor-ui:latest
|
|
container_name: supervisor-ui
|
|
restart: unless-stopped
|
|
environment:
|
|
- SUPERVISOR_HOST=${SUPERVISOR_HOST}
|
|
- SUPERVISOR_PORT=${SUPERVISOR_PORT}
|
|
- SUPERVISOR_USERNAME=${SUPERVISOR_USERNAME}
|
|
- SUPERVISOR_PASSWORD=${SUPERVISOR_PASSWORD}
|
|
networks:
|
|
- supervisor-network
|
|
healthcheck:
|
|
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/api/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 5s
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.http.routers.supervisor-ui.rule=Host(`supervisor.yourdomain.com`)"
|
|
- "traefik.http.services.supervisor-ui.loadbalancer.server.port=3000"
|
|
|
|
networks:
|
|
supervisor-network:
|
|
external: true
|
|
```
|
|
|
|
## Security Considerations
|
|
|
|
### 1. Credentials Management
|
|
|
|
**Bad** (credentials in image):
|
|
```bash
|
|
docker build --build-arg SUPERVISOR_PASSWORD=secret .
|
|
```
|
|
|
|
**Good** (credentials at runtime):
|
|
```bash
|
|
docker run -e SUPERVISOR_PASSWORD=secret ...
|
|
```
|
|
|
|
**Better** (use secrets management):
|
|
```bash
|
|
# Docker Swarm secrets
|
|
echo "secret_password" | docker secret create supervisor_password -
|
|
```
|
|
|
|
### 2. Network Isolation
|
|
|
|
Use Docker networks to isolate services:
|
|
|
|
```yaml
|
|
services:
|
|
supervisor-ui:
|
|
networks:
|
|
- internal # Only accessible from other containers
|
|
|
|
nginx:
|
|
networks:
|
|
- internal
|
|
- public # Exposed to internet
|
|
```
|
|
|
|
### 3. Read-only Filesystem
|
|
|
|
For enhanced security, run with read-only root filesystem:
|
|
|
|
```bash
|
|
docker run -d \
|
|
--read-only \
|
|
--tmpfs /tmp \
|
|
-e SUPERVISOR_HOST=supervisor.local \
|
|
dev.pivoine.art/valknar/supervisor-ui:latest
|
|
```
|
|
|
|
### 4. Resource Limits
|
|
|
|
Prevent resource exhaustion:
|
|
|
|
```yaml
|
|
services:
|
|
supervisor-ui:
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: '0.5'
|
|
memory: 256M
|
|
reservations:
|
|
cpus: '0.25'
|
|
memory: 128M
|
|
```
|
|
|
|
## Monitoring & Health Checks
|
|
|
|
### Health Check Endpoint
|
|
|
|
The application provides a health check at `/api/health`:
|
|
|
|
```bash
|
|
curl http://localhost:3000/api/health
|
|
# {"status":"healthy","timestamp":"2025-11-23T17:00:00.000Z"}
|
|
```
|
|
|
|
### Docker Health Check
|
|
|
|
Built-in health check monitors the API:
|
|
|
|
```bash
|
|
docker inspect supervisor-ui --format='{{.State.Health.Status}}'
|
|
```
|
|
|
|
### Integration with Monitoring Tools
|
|
|
|
#### Prometheus
|
|
|
|
Add health check as a target in `prometheus.yml`:
|
|
|
|
```yaml
|
|
scrape_configs:
|
|
- job_name: 'supervisor-ui'
|
|
metrics_path: '/api/health'
|
|
static_configs:
|
|
- targets: ['supervisor-ui:3000']
|
|
```
|
|
|
|
#### Uptime Kuma
|
|
|
|
Add HTTP(s) monitor:
|
|
- Monitor Type: HTTP(s)
|
|
- URL: `http://supervisor-ui:3000/api/health`
|
|
- Interval: 60 seconds
|
|
|
|
## Troubleshooting
|
|
|
|
### Container won't start
|
|
|
|
Check logs:
|
|
```bash
|
|
docker logs supervisor-ui
|
|
```
|
|
|
|
Common issues:
|
|
- Port 3000 already in use
|
|
- Invalid environment variables
|
|
- Network connectivity issues
|
|
|
|
### Can't connect to Supervisor
|
|
|
|
1. Check if Supervisor is accessible:
|
|
```bash
|
|
# From host
|
|
curl http://supervisor-host:9001/RPC2
|
|
|
|
# From container
|
|
docker exec supervisor-ui curl http://supervisor-host:9001/RPC2
|
|
```
|
|
|
|
2. Verify Supervisor configuration in `/etc/supervisor/supervisord.conf`:
|
|
```ini
|
|
[inet_http_server]
|
|
port = *:9001 ; Listen on all interfaces, not just 127.0.0.1
|
|
```
|
|
|
|
3. Check environment variables:
|
|
```bash
|
|
docker exec supervisor-ui env | grep SUPERVISOR
|
|
```
|
|
|
|
### Authentication failures
|
|
|
|
Verify credentials match your Supervisor configuration:
|
|
|
|
```bash
|
|
# Test with curl
|
|
curl -u username:password http://supervisor-host:9001/RPC2
|
|
```
|
|
|
|
## Rollback Strategy
|
|
|
|
### Quick Rollback to Previous Version
|
|
|
|
```bash
|
|
# Stop current version
|
|
docker stop supervisor-ui
|
|
docker rm supervisor-ui
|
|
|
|
# Run previous version
|
|
docker run -d \
|
|
--name supervisor-ui \
|
|
-p 3000:3000 \
|
|
--env-file .env \
|
|
dev.pivoine.art/valknar/supervisor-ui:v1.0.0 # Specific version
|
|
```
|
|
|
|
### Using Docker Compose
|
|
|
|
```bash
|
|
# Edit docker-compose.yml to use previous tag
|
|
# Then:
|
|
docker-compose up -d --force-recreate
|
|
```
|
|
|
|
## Backup & Recovery
|
|
|
|
### No Persistent Data
|
|
|
|
The Supervisor UI is stateless and doesn't store any data. All configuration is in environment variables.
|
|
|
|
To "backup" your deployment:
|
|
1. Save your `.env` file or docker-compose.yml
|
|
2. Document your Supervisor connection details
|
|
|
|
To "restore":
|
|
1. Pull the image
|
|
2. Apply your saved environment configuration
|
|
3. Start the container
|
|
|
|
## Performance Tuning
|
|
|
|
### Node.js Optimization
|
|
|
|
For better performance in production:
|
|
|
|
```bash
|
|
docker run -d \
|
|
-e NODE_OPTIONS="--max-old-space-size=256" \
|
|
-e SUPERVISOR_HOST=supervisor.local \
|
|
dev.pivoine.art/valknar/supervisor-ui:latest
|
|
```
|
|
|
|
### Caching Strategies
|
|
|
|
The application uses React Query with intelligent caching:
|
|
- System info: 5 second stale time
|
|
- Processes: 3 second stale time
|
|
- Logs: 2 second stale time
|
|
|
|
These are optimized for real-time monitoring while minimizing API calls.
|
|
|
|
## Scaling Considerations
|
|
|
|
### Horizontal Scaling
|
|
|
|
The application is stateless and can be scaled horizontally:
|
|
|
|
```yaml
|
|
services:
|
|
supervisor-ui:
|
|
image: dev.pivoine.art/valknar/supervisor-ui:latest
|
|
deploy:
|
|
replicas: 3
|
|
update_config:
|
|
parallelism: 1
|
|
delay: 10s
|
|
restart_policy:
|
|
condition: on-failure
|
|
```
|
|
|
|
### Load Balancing
|
|
|
|
Use nginx or Traefik to load balance across replicas:
|
|
|
|
```nginx
|
|
upstream supervisor_ui {
|
|
server supervisor-ui-1:3000;
|
|
server supervisor-ui-2:3000;
|
|
server supervisor-ui-3:3000;
|
|
}
|
|
|
|
server {
|
|
location / {
|
|
proxy_pass http://supervisor_ui;
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
**Last Updated**: November 23, 2025
|
|
**Version**: 0.1.0
|