Advanced Configuration¶
This guide covers advanced configuration topics including dependency management, service ordering, and complex multi-service scenarios.
Dependency Management¶
Go Overlay supports explicit service dependencies using the depends_on field. This ensures services start in the correct order and that dependent services wait for their dependencies to be ready.
Basic Dependencies¶
Use depends_on to specify which services must be running before a service starts:
[[services]]
name = "database"
command = "/usr/bin/postgres"
[[services]]
name = "web-app"
command = "/app/server"
depends_on = ["database"] # Wait for database to be RUNNING
Behavior: - web-app will not start until database reaches RUNNING state - If database fails to start, web-app will not start - Subject to dependency_wait_timeout configuration
Multiple Dependencies¶
A service can depend on multiple other services:
[[services]]
name = "database"
command = "/usr/bin/postgres"
[[services]]
name = "cache"
command = "/usr/bin/redis-server"
[[services]]
name = "web-app"
command = "/app/server"
depends_on = ["database", "cache"] # Wait for both
Behavior: - web-app waits for both database and cache to be RUNNING - Dependencies can start in parallel - web-app starts only after all dependencies are ready
Dependency Chains¶
Dependencies can form chains where services depend on other dependent services:
[[services]]
name = "database"
command = "/usr/bin/postgres"
[[services]]
name = "migration"
command = "/app/migrate"
depends_on = ["database"]
[[services]]
name = "web-app"
command = "/app/server"
depends_on = ["migration"] # Indirectly depends on database
Startup order: 1. database starts first 2. migration starts after database is RUNNING 3. web-app starts after migration is RUNNING
Wait After Delays¶
The wait_after field adds a delay after a service starts before dependent services begin starting. This is useful for services that need initialization time after the process starts.
Basic Wait After¶
[[services]]
name = "database"
command = "/usr/bin/postgres"
wait_after = 10 # Wait 10 seconds after starting
[[services]]
name = "web-app"
command = "/app/server"
depends_on = ["database"]
Behavior: 1. database process starts 2. database reaches RUNNING state 3. Wait 10 seconds 4. web-app can now start
When to Use wait_after¶
Use wait_after when: - Service needs time to initialize after process starts - Service listens on a port but isn't ready immediately - Service needs to warm up caches or load data - Service performs internal health checks
Examples:
Database initialization:
[[services]]
name = "postgres"
command = "/usr/bin/postgres"
wait_after = 15 # Time to initialize database, load extensions
Application warm-up:
[[services]]
name = "java-app"
command = "/usr/bin/java"
args = ["-jar", "app.jar"]
wait_after = 30 # JVM startup and application initialization
Cache population:
[[services]]
name = "redis"
command = "/usr/bin/redis-server"
wait_after = 5 # Time to load RDB file from disk
Circular Dependency Detection¶
Go Overlay automatically detects circular dependencies and prevents system startup if found.
What is a Circular Dependency?¶
A circular dependency occurs when services depend on each other in a cycle:
# ❌ This configuration will fail
[[services]]
name = "service-a"
depends_on = ["service-b"]
[[services]]
name = "service-b"
depends_on = ["service-a"]
Error: Go Overlay will detect this cycle and exit with an error during startup.
Complex Circular Dependencies¶
Cycles can involve multiple services:
# ❌ This configuration will fail
[[services]]
name = "service-a"
depends_on = ["service-c"]
[[services]]
name = "service-b"
depends_on = ["service-a"]
[[services]]
name = "service-c"
depends_on = ["service-b"]
Cycle: A → C → B → A
Avoiding Circular Dependencies¶
Problem: Two services that need each other
Solution 1: Remove the dependency
# If services can start independently, remove depends_on
[[services]]
name = "service-a"
command = "/app/service-a"
[[services]]
name = "service-b"
command = "/app/service-b"
Solution 2: Introduce an intermediary
# Use a shared dependency instead
[[services]]
name = "shared-resource"
command = "/app/resource"
[[services]]
name = "service-a"
depends_on = ["shared-resource"]
[[services]]
name = "service-b"
depends_on = ["shared-resource"]
Solution 3: Use wait_after without depends_on
# Start both, but delay one
[[services]]
name = "service-a"
command = "/app/service-a"
[[services]]
name = "service-b"
command = "/app/service-b"
wait_after = 10 # Give service-a time to start
Complex Multi-Service Examples¶
Example 1: Web Application Stack¶
Complete stack with database, cache, worker, and web server:
[timeouts]
post_script_timeout = 30
service_shutdown_timeout = 60
global_shutdown_timeout = 180
dependency_wait_timeout = 300
# Database - Foundation service
[[services]]
name = "postgres"
command = "/usr/lib/postgresql/15/bin/postgres"
args = ["-D", "/var/lib/postgresql/data"]
pre_script = "/scripts/init-postgres.sh"
user = "postgres"
enabled = true
required = true
wait_after = 10 # Database needs time to initialize
# Cache - Independent of database
[[services]]
name = "redis"
command = "/usr/bin/redis-server"
args = ["/etc/redis/redis.conf"]
user = "redis"
enabled = true
required = false
wait_after = 3
# Database migrations - Depends on database
[[services]]
name = "db-migration"
command = "/app/migrate"
args = ["up"]
depends_on = ["postgres"]
user = "app"
enabled = true
required = true
# Background worker - Depends on database and cache
[[services]]
name = "celery-worker"
command = "/app/venv/bin/celery"
args = ["-A", "tasks", "worker", "--loglevel=info"]
depends_on = ["postgres", "redis", "db-migration"]
user = "app"
enabled = true
required = false
# Web application - Depends on migrations
[[services]]
name = "web-app"
command = "/app/venv/bin/gunicorn"
args = ["app:app", "--bind", "0.0.0.0:8000", "--workers", "4"]
depends_on = ["db-migration", "redis"]
pos_script = "/app/healthcheck.sh"
user = "app"
enabled = true
required = true
wait_after = 5
# Reverse proxy - Depends on web app
[[services]]
name = "nginx"
command = "/usr/sbin/nginx"
args = ["-g", "daemon off;"]
depends_on = ["web-app"]
user = "www-data"
enabled = true
required = true
Startup sequence: 1. postgres and redis start in parallel (no dependencies) 2. Wait 10 seconds for postgres, 3 seconds for redis 3. db-migration starts (depends on postgres) 4. celery-worker and web-app start after migration completes 5. Wait 5 seconds for web-app 6. nginx starts last (depends on web-app)
Example 2: Microservices with Service Mesh¶
Multiple microservices with a service mesh proxy:
# Service mesh control plane
[[services]]
name = "consul"
command = "/usr/bin/consul"
args = ["agent", "-dev"]
enabled = true
required = true
wait_after = 5
# Service mesh proxy
[[services]]
name = "envoy"
command = "/usr/bin/envoy"
args = ["-c", "/etc/envoy/envoy.yaml"]
depends_on = ["consul"]
enabled = true
required = true
wait_after = 3
# Microservice 1
[[services]]
name = "user-service"
command = "/app/user-service"
depends_on = ["envoy"]
user = "app"
enabled = true
required = true
# Microservice 2
[[services]]
name = "order-service"
command = "/app/order-service"
depends_on = ["envoy"]
user = "app"
enabled = true
required = true
# API Gateway
[[services]]
name = "api-gateway"
command = "/app/gateway"
depends_on = ["user-service", "order-service"]
user = "app"
enabled = true
required = true
Example 3: Data Pipeline¶
ETL pipeline with staged processing:
# Data source
[[services]]
name = "kafka"
command = "/opt/kafka/bin/kafka-server-start.sh"
args = ["/opt/kafka/config/server.properties"]
user = "kafka"
enabled = true
required = true
wait_after = 15
# Stream processor
[[services]]
name = "flink"
command = "/opt/flink/bin/taskmanager.sh"
args = ["start-foreground"]
depends_on = ["kafka"]
user = "flink"
enabled = true
required = true
wait_after = 10
# Data warehouse
[[services]]
name = "clickhouse"
command = "/usr/bin/clickhouse-server"
args = ["--config-file=/etc/clickhouse-server/config.xml"]
user = "clickhouse"
enabled = true
required = true
wait_after = 10
# ETL job
[[services]]
name = "etl-job"
command = "/app/etl"
depends_on = ["flink", "clickhouse"]
user = "app"
enabled = true
required = false
Example 4: Development Environment¶
Local development stack with hot-reload:
# Database
[[services]]
name = "postgres-dev"
command = "/usr/bin/postgres"
args = ["-D", "/tmp/pgdata"]
enabled = true
required = false
wait_after = 5
# Frontend dev server
[[services]]
name = "vite"
command = "/usr/bin/npm"
args = ["run", "dev"]
enabled = true
required = false
# Backend dev server
[[services]]
name = "backend-dev"
command = "/app/venv/bin/python"
args = ["manage.py", "runserver"]
depends_on = ["postgres-dev"]
enabled = true
required = false
Dependency Best Practices¶
1. Minimize Dependencies¶
Only use depends_on when truly necessary:
# ✅ Good: Only essential dependencies
[[services]]
name = "web-app"
depends_on = ["database"]
# ❌ Avoid: Unnecessary dependencies
[[services]]
name = "web-app"
depends_on = ["database", "cache", "metrics", "logging"]
2. Use wait_after Appropriately¶
Add delays only when needed:
# ✅ Good: Database needs initialization time
[[services]]
name = "postgres"
wait_after = 10
# ❌ Avoid: Unnecessary delay
[[services]]
name = "nginx"
wait_after = 30 # Nginx starts instantly
3. Keep Dependency Chains Short¶
Avoid deep dependency chains:
# ✅ Good: Flat structure
[[services]]
name = "app"
depends_on = ["database", "cache"]
# ❌ Avoid: Deep chain
# A → B → C → D → E
4. Mark Critical Services as Required¶
Use required = true for services that must run:
[[services]]
name = "database"
required = true # System can't function without it
[[services]]
name = "metrics"
required = false # Nice to have, but not critical
5. Test Dependency Ordering¶
Verify startup order works correctly:
# Start system and watch logs
go-overlay | grep "Starting service"
# Should see services start in correct order
Troubleshooting Dependencies¶
Service Won't Start - Waiting for Dependencies¶
Symptom: Service stuck in PENDING state
Check: 1. Are dependencies running? go-overlay list 2. Is dependency_wait_timeout sufficient? 3. Are dependency names spelled correctly?
Slow Startup¶
Symptom: System takes long time to start
Solutions: 1. Reduce unnecessary wait_after delays 2. Remove unnecessary dependencies 3. Start independent services in parallel
Circular Dependency Error¶
Symptom: System won't start, circular dependency detected
Solution: 1. Review dependency graph 2. Remove or restructure dependencies 3. Use wait_after instead of depends_on if appropriate
Next Steps¶
- Review service configuration options
- Understand timeout behavior
- See complete examples
- Learn about service lifecycle