Service Registry
The Service Registry is the central configuration file that defines all machines, services, and their relationships in your Portoser cluster.
Overview
The registry is stored as a YAML file (registry.yml) and serves as the single source of truth for:
- Machines: Physical or virtual machines where services run
- Services: Applications and their configurations
- Dependencies: Relationships between services
- Health Checks: How to verify service health
- Secrets: References to Vault secrets
Registry Structure
machines:
machine-name:
host: "IP or hostname"
user: "SSH username"
platform: "darwin|linux|bsd"
vault_role: "vault-approle-id"
ssh_key: "/path/to/key"
services:
service-name:
type: "docker|local|native"
path: "/absolute/path/to/service"
machine: "machine-name"
port: 8080
health:
endpoint: "/health"
type: "http|tcp"
interval: 30
depends_on:
- other-service
env:
KEY: "value"
vault_secrets:
- path: "secret/data/myapp"
keys: ["api_key", "database_url"]
Machines Section
Required Fields
- host: IP address or hostname of the machine
- user: SSH username for connecting to the machine
- platform: Operating system type (
darwin,linux, orbsd)
Optional Fields
- ssh_key: Path to SSH private key (defaults to
~/.ssh/id_rsa) - vault_role: Vault AppRole ID for this machine
- port: SSH port (defaults to 22)
- description: Human-readable description
- tags: Array of tags for organization
Example
machines:
m1:
host: "192.168.0.245"
user: "admin"
platform: "darwin"
vault_role: "m1-approle"
description: "Main server - macOS"
tags: ["production", "macos"]
mini1:
host: "192.168.0.96"
user: "ubuntu"
platform: "linux"
vault_role: "mini1-approle"
description: "Ubuntu mini server"
tags: ["production", "linux"]
Services Section
Service Types
Docker Services (type: docker)
For Docker Compose applications:
services:
my-docker-app:
type: "docker"
path: "/opt/my-app"
machine: "m1"
compose_file: "docker-compose.yml" # optional, defaults to docker-compose.yml
env_file: ".env" # optional
Portoser will:
- Look for
docker-compose.ymlin the specified path - Run
docker-compose up -dto start the service - Monitor container health
Local Services (type: local)
For Python applications managed by UV:
services:
my-python-app:
type: "local"
path: "/opt/my-python-app"
machine: "m1"
command: "uv run python main.py"
port: 8080
log_file: "/var/log/my-app.log" # optional
Portoser will:
- Set up Python virtual environment with UV
- Run the specified command
- Manage the process lifecycle
- Capture logs
Native Services (type: native)
For system services (systemd/launchd):
services:
my-system-service:
type: "native"
path: "/opt/my-service"
machine: "m1"
service_name: "my-service"
command: "./start.sh"
Portoser will:
- Generate systemd/launchd service file
- Register with system service manager
- Enable auto-start on boot
Health Checks
Define how Portoser should check if your service is healthy:
HTTP Health Check
services:
web-app:
health:
type: "http"
endpoint: "/health"
port: 8080
interval: 30 # seconds between checks
timeout: 5 # request timeout
retries: 3 # failed attempts before marking unhealthy
expected_status: 200 # expected HTTP status code
TCP Health Check
services:
database:
health:
type: "tcp"
port: 5432
interval: 15
timeout: 3
retries: 2
Process Health Check
services:
background-worker:
health:
type: "process"
pid_file: "/var/run/worker.pid"
interval: 60
Dependencies
Define service dependencies to ensure proper startup order:
services:
web-app:
depends_on:
- database
- redis
- vault
database:
# No dependencies
redis:
# No dependencies
Portoser will:
- Start dependencies before dependent services
- Check dependency health before deploying
- Prevent starting a service if dependencies are unhealthy
Environment Variables
Inline Environment Variables
services:
my-app:
env:
NODE_ENV: "production"
PORT: "3000"
LOG_LEVEL: "info"
Environment File
services:
my-app:
env_file: ".env.production"
Vault Secrets
Inject secrets from HashiCorp Vault:
services:
my-app:
vault_secrets:
- path: "secret/data/myapp"
keys:
- api_key
- database_url
- jwt_secret
- path: "secret/data/shared"
keys:
- smtp_password
Portoser will:
- Fetch secrets from Vault using machine's AppRole
- Inject as environment variables
- Handle secret rotation
Port Mapping
services:
web-app:
port: 8080 # Primary port
ports: # Multiple ports
- 8080
- 8443
port_mapping: # Custom mapping
host: 80
container: 8080
Resource Limits
For Docker services:
services:
my-app:
resources:
memory: "512M"
cpus: "0.5"
disk: "1G"
Registry Management
Via CLI
Add a Machine
portoser machine add \
--name m1 \
--host 192.168.0.245 \
--user admin \
--platform darwin
Add a Service
portoser service add \
--name my-app \
--type docker \
--path /opt/my-app \
--machine m1
Update Service
portoser service update my-app \
--machine mini1 \
--port 9000
Remove Service
portoser service remove my-app
Via Web UI
- Navigate to Services or Machines tab
- Click Add New button
- Fill in the form
- Click Save
To move a service:
- Drag the service card
- Drop on target machine
- Confirm the move
Direct YAML Editing
You can edit registry.yml directly:
# Edit the registry
nano registry.yml
# Validate the registry
portoser registry validate
# Reload the registry
portoser registry reload
Best Practices
1. Use Descriptive Names
# Good
services:
api-gateway-production:
user-service-staging:
postgres-primary:
# Avoid
services:
app1:
service2:
db:
2. Organize with Tags
machines:
m1:
tags: ["production", "macos", "high-memory"]
services:
my-app:
tags: ["production", "api", "customer-facing"]
3. Document Dependencies
services:
api:
depends_on:
- postgres # Required for data storage
- redis # Required for caching
- vault # Required for secrets
4. Use Health Checks
Always define health checks for critical services:
services:
critical-api:
health:
type: "http"
endpoint: "/health"
interval: 15 # Check frequently
retries: 2 # Fail fast
5. Vault for Secrets
Never store secrets in the registry:
# Bad
services:
my-app:
env:
API_KEY: "secret-key-123" # DON'T DO THIS
# Good
services:
my-app:
vault_secrets:
- path: "secret/data/myapp"
keys: ["api_key"]
6. Version Control
Keep your registry in version control:
git add registry.yml
git commit -m "Add new service: my-app"
git push
7. Backup Regularly
# Backup the registry
cp registry.yml registry.yml.backup
# Or use git
git add registry.yml
git commit -m "Backup registry"
Registry Validation
Validate your registry before applying changes:
# Validate syntax and structure
portoser registry validate
# Dry-run deployment
portoser deploy my-app --dry-run
# Check for conflicts
portoser registry check-conflicts
Common validation errors:
- Duplicate names: Service or machine names must be unique
- Missing machines: Service references non-existent machine
- Circular dependencies: Services depend on each other
- Invalid types: Unknown service type
- Port conflicts: Multiple services on same port
- Missing required fields: host, user, platform, etc.
Advanced Features
Dynamic Port Assignment
services:
my-app:
port: "auto" # Portoser assigns available port
Conditional Configuration
services:
my-app:
env:
NODE_ENV: "{{ ENVIRONMENT }}" # Template variable
Service Groups
groups:
web-stack:
- nginx
- web-app
- redis
services:
nginx:
group: "web-stack"
web-app:
group: "web-stack"
Deploy a group:
portoser deploy --group web-stack
Troubleshooting
Registry Not Loading
# Check syntax
portoser registry validate
# Check file permissions
ls -la registry.yml
# Check YAML syntax
yamllint registry.yml
Service Not Found
# List all services
portoser service list
# Check registry path
echo $REGISTRY_FILE
# Reload registry
portoser registry reload
Machine Connection Failed
# Test SSH
ssh user@machine-host
# Check machine config
portoser machine show m1
# Update machine details
portoser machine update m1 --host new-ip
Next Steps
- Deployment Types —
dockervslocalvsnative - Self-Healing Loop — what runs on every deploy
- Vault Integration — secret references in the registry
- Health Monitoring — service health