Configuration Reference
Complete reference for iop.yml configuration options
Configuration Reference
Complete reference for all configuration options available in iop.yml.
Basic Structure
name: my-project # Required: Project name used for Docker networks and container naming
# Global settings
ssh: # SSH connection settings
  username: iop # SSH username (default from config)
  port: 22 # SSH port (default: 22)
  key_file: ~/.ssh/id_rsa # Path to SSH private key (optional)
proxy: # Global proxy settings
  image: elitan/iop-proxy:latest # Custom proxy image (optional)
apps: # Applications (zero-downtime deployments)
  web: # App name
    # App configuration here
services: # Services (direct replacement deployments)
  database: # Service name  
    # Service configuration hereApps Configuration
Apps are user-facing applications that get zero-downtime blue-green deployments. They are built locally and transferred via SSH.
Basic App Configuration
apps:
  web:
    server: server1.com # Target server (single server per app)
    
    build: # Build configuration (for local builds)
      context: . # Build context (default: .)
      dockerfile: Dockerfile # Dockerfile path (default: Dockerfile)
      args: # Build arguments from environment
        - NODE_ENV
        - API_URL
      target: production # Multi-stage build target (optional)
      platform: linux/amd64 # Target platform (optional)
    proxy: # Reverse proxy configuration
      app_port: 3000 # Port your app runs on inside container
      hosts: # Custom domains (optional)
        - example.com
        - www.example.com
      ssl: true # Enable HTTPS (default: true)
      ssl_redirect: true # Redirect HTTP to HTTPS (optional)
      forward_headers: false # Forward X-Forwarded-* headers (optional)
      response_timeout: 30s # Request timeout (default: 30s)
    environment: # Environment variables
      plain: # Plain text variables (KEY=VALUE format)
        - NODE_ENV=production
        - PORT=3000
      secret: # Variables from .iop/secrets (KEY format)
        - DATABASE_URL
        - API_KEY
    health_check: # Health check configuration
      path: /up # Health endpoint (default: /up)
    replicas: 2 # Number of replicas (default: 1)Advanced App Options
apps:
  api:
    server: api.example.com
    image: my-app/api # Docker image name (optional, auto-generated)
    
    # Multiple replicas for load balancing
    replicas: 3
    # Port mapping (rarely needed since proxy handles routing)
    ports:
      - "8080:8080"
    # Volume mounts
    volumes:
      - ./logs:/app/logs
      - app_data:/data
    # Custom registry (for pre-built images instead of local build)
    registry:
      url: ghcr.io # Registry URL (optional)
      username: myuser # Registry username
      password_secret: REGISTRY_PASSWORD # Password from .iop/secrets
    # Override default command
    command: "npm start --production"Services Configuration
Services are infrastructure components (databases, caches, etc.) that get direct replacement during deployment. They use pre-built Docker images.
Basic Service Configuration
services:
  postgres:
    image: postgres:17 # Required: Docker image with tag
    server: db.example.com # Target server
    
    ports: # Port mapping
      - "5432:5432"
    
    volumes: # Persistent volumes
      - ./pgdata:/var/lib/postgresql/data
      - db_logs:/var/log/postgresql
    
    environment: # Environment variables
      plain:
        - POSTGRES_USER=postgres
        - POSTGRES_DB=myapp
      secret:
        - POSTGRES_PASSWORD # From .iop/secretsAdvanced Service Options
services:
  redis:
    image: redis:7-alpine
    server: cache.example.com
    
    # Custom registry for private images
    registry:
      url: private-registry.com
      username: myuser
      password_secret: REGISTRY_TOKEN
    
    # Override default command
    command: "redis-server --appendonly yes"
    
    # Multiple port mappings
    ports:
      - "6379:6379"
      - "16379:16379" # Sentinel port
    
    # Named and bind volumes
    volumes:
      - redis_data:/data # Named volume
      - ./redis.conf:/usr/local/etc/redis/redis.conf:ro # Bind mount (read-only)Global Configuration
SSH Configuration
ssh:
  username: iop # Default SSH username for all servers
  port: 22 # Default SSH port
  key_file: ~/.ssh/id_rsa # Path to SSH private keyYou can override SSH settings per server by using different usernames in your app/service server specifications.
Proxy Configuration
proxy:
  image: elitan/iop-proxy:latest # Custom proxy Docker imageThe proxy handles:
- SSL certificate management (Let's Encrypt)
 - HTTP to HTTPS redirects
 - Reverse proxy routing
 - Load balancing between app replicas
 
Environment Variables
Plain Environment Variables
environment:
  plain:
    - NODE_ENV=production # Simple key=value
    - DEBUG=app:* # Can contain special characters
    - PORT=3000Secret Environment Variables
Sensitive values are stored in .iop/secrets and referenced by key name:
In iop.yml:
environment:
  secret:
    - DATABASE_URL
    - JWT_SECRET
    - STRIPE_API_KEYIn .iop/secrets:
DATABASE_URL=postgres://user:password@localhost:5432/myapp
JWT_SECRET=your-super-secret-jwt-key
STRIPE_API_KEY=sk_live_...Build Arguments
For apps with build configuration, you can pass environment variables as build arguments:
apps:
  web:
    build:
      context: .
      args:
        - NODE_ENV # Will pass NODE_ENV from environment section
        - API_URL
    environment:
      plain:
        - NODE_ENV=production
        - API_URL=https://api.example.comHealth Checks
Apps should implement health check endpoints for zero-downtime deployments:
apps:
  web:
    health_check:
      path: /up # Default endpointYour application must respond with HTTP 200 at this endpoint when healthy:
// Express.js example
app.get('/up', (req, res) => {
  // Check database connectivity, etc.
  res.status(200).send('OK');
});// Go example
http.HandleFunc("/up", func(w http.ResponseWriter, r *http.Request) {
    // Check dependencies
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("OK"))
})Volumes
Named Volumes
volumes:
  - postgres_data:/var/lib/postgresql/data
  - app_logs:/app/logsBind Mounts
volumes:
  - ./data:/app/data # Relative to iop.yml location
  - /host/path:/container/path # Absolute paths
  - ./config/nginx.conf:/etc/nginx/nginx.conf:ro # Read-onlyVolume Best Practices
- Use named volumes for database data
 - Use bind mounts for configuration files
 - Ensure host directories exist and have proper permissions
 - Use read-only (
:ro) for configuration files 
Multi-Server Deployment
Each app/service can target a single server. For multi-server deployments, create multiple entries:
apps:
  web-primary:
    server: server1.com
    # ... config
    
  web-secondary:
    server: server2.com
    # ... same config
    
  web-cdn:
    server: cdn.server.com
    # ... same config with different domains
    proxy:
      hosts:
        - cdn.example.comRegistry Configuration
Global Registry
docker:
  registry: ghcr.io
  username: myuser
  # Password should be in .iop/secrets as DOCKER_REGISTRY_PASSWORDPer-App/Service Registry
apps:
  web:
    registry:
      url: private-registry.com
      username: appuser
      password_secret: APP_REGISTRY_TOKENRegistry configuration is only needed for:
- Services using private images
 - Apps using pre-built images (instead of local build)
 
Apps with build configuration don't need registries (images are built locally and transferred via SSH).
Complete Example
Here's a complete example showing all major features:
name: my-fullstack-app
ssh:
  username: iop
  key_file: ~/.ssh/deployment_key
apps:
  web:
    server: web.example.com
    replicas: 2
    build:
      context: .
      dockerfile: Dockerfile.web
      args:
        - NODE_ENV
        - API_URL
    proxy:
      hosts:
        - myapp.com
        - www.myapp.com
      app_port: 3000
      ssl: true
    environment:
      plain:
        - NODE_ENV=production
        - API_URL=https://api.myapp.com
      secret:
        - JWT_SECRET
        - DATABASE_URL
    health_check:
      path: /health
    volumes:
      - ./logs:/app/logs
  api:
    server: api.example.com
    build:
      context: ./api
      dockerfile: Dockerfile
    proxy:
      hosts:
        - api.myapp.com
      app_port: 8000
    environment:
      plain:
        - NODE_ENV=production
        - CORS_ORIGIN=https://myapp.com
      secret:
        - DATABASE_URL
        - JWT_SECRET
        - STRIPE_SECRET_KEY
    health_check:
      path: /api/health
services:
  postgres:
    image: postgres:17
    server: db.example.com
    ports:
      - "5432:5432"
    environment:
      plain:
        - POSTGRES_USER=postgres
        - POSTGRES_DB=myapp
      secret:
        - POSTGRES_PASSWORD
    volumes:
      - postgres_data:/var/lib/postgresql/data
  redis:
    image: redis:7-alpine
    server: cache.example.com
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    command: "redis-server --appendonly yes"Configuration Validation
iop validates your configuration and provides helpful error messages:
- Missing required fields - Clear error messages about what's missing
 - Invalid formats - Validation errors with suggestions
 - Reserved names - Apps/services cannot be named: 
init,status,proxy 
Use iop --verbose to see detailed configuration validation information.
Environment-Specific Configurations
Create multiple configuration files for different environments:
# Development
iop.development.yml
# Staging  
iop.staging.yml
# Production
iop.production.ymlDeploy with specific configurations:
iop -c iop.staging.yml    # Deploy to staging
iop -c iop.production.yml # Deploy to productionThis allows different:
- Server targets
 - Environment variables
 - Replica counts
 - SSL settings
 - Domain configurations