Development
Architecture
Understand how Odysseus works under the hood.
Overview
Odysseus is a deployment orchestration tool that manages Docker containers via SSH. It consists of two Ruby gems:
- odysseus-core: Core functionality (config parsing, Docker/Caddy clients, deployment logic)
- odysseus-cli: Command-line interface
System architecture
┌─────────────────────────────────────────────────────────────────┐
│ Local Machine │
│ ┌─────────────┐ ┌──────────────┐ ┌───────────────────┐ │
│ │ deploy.yml │───▶│ odysseus-cli │───▶│ odysseus-core │ │
│ └─────────────┘ └──────────────┘ └─────────┬─────────┘ │
│ │ │
│ ┌─────────────────┐ │ │
│ │ secrets.yml.enc │──────────────────────────────┤ │
│ └─────────────────┘ │ │
└───────────────────────────────────────────────────┼─────────────┘
│
SSH │
▼
┌─────────────────────────────────────────────────────────────────┐
│ Target Server │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Docker │ │
│ │ ┌────────────────┐ ┌────────────────┐ ┌────────────┐ │ │
│ │ │ myapp-web-v1.0 │ │ myapp-jobs-v1.0│ │ myapp-db │ │ │
│ │ └────────────────┘ └────────────────┘ └────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Caddy (Reverse Proxy) │ │
│ │ │ │
│ │ myapp.example.com ──────────▶ myapp-web-v1.0:3000 │ │
│ │ │ │
│ │ Automatic HTTPS via Let's Encrypt │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Core components
Config parser
Reads and validates deploy.yml:
lib/odysseus/config/parser.rb
lib/odysseus/validators/config.rb
Normalizes configuration into internal structures and validates required fields.
SSH client
Manages SSH connections to servers:
lib/odysseus/deployer/ssh.rb
Uses net-ssh for secure connections with key-based authentication.
Docker client
Wraps Docker CLI commands:
lib/odysseus/docker/client.rb
Handles:
- Container lifecycle (run, stop, remove)
- Image management (pull, push, save, load)
- Health checks and status
Caddy client
Manages Caddy reverse proxy:
lib/odysseus/caddy/client.rb
Uses Caddy's admin API to:
- Add/remove routes dynamically
- Configure upstream servers
- Manage SSL certificates
Builder
Builds Docker images:
lib/odysseus/builder/client.rb
Supports:
- Local builds
- Remote builds via SSH
- Multi-architecture builds
Secrets manager
Handles encrypted secrets:
lib/odysseus/secrets/encrypted_file.rb
lib/odysseus/secrets/loader.rb
Uses AES-256-GCM for encryption/decryption.
Host providers
Resolves deployment targets:
lib/odysseus/host_providers/base.rb
lib/odysseus/host_providers/static.rb
lib/odysseus/host_providers/aws_asg.rb
Supports static hosts and AWS Auto Scaling Group discovery.
Orchestrator
Coordinates deployments:
lib/odysseus/orchestrator/web_deploy.rb
lib/odysseus/orchestrator/job_deploy.rb
lib/odysseus/orchestrator/accessory_deploy.rb
Implements the deployment sequence for each role type.
Deployment flow
1. Configuration loading
deploy.yml → Parser → Normalized Config
↓
Validator → Errors/Warnings
2. Host resolution
servers.web.hosts → Static Provider → Host List
servers.web.aws → AWS Provider → Host List
3. Secrets decryption
ODYSSEUS_MASTER_KEY + secrets.yml.enc → Decrypted Secrets
4. Per-host deployment
For each server in each role:
1. Connect via SSH
2. Pull/load Docker image
3. Start new container
4. Wait for health check
5. Update Caddy routing (web only)
6. Drain old container
7. Stop old container
8. Cleanup old containers
Key design decisions
SSH-based
All operations happen over SSH. No agents or daemons required on servers.
Benefits:
- Simple setup (just SSH access)
- No additional security surface
- Works with any server
Caddy for routing
Caddy provides automatic HTTPS and dynamic configuration.
Benefits:
- Automatic Let's Encrypt certificates
- Dynamic upstream management via API
- Zero-downtime routing updates
Container-per-deploy
Each deployment creates a new container with a unique name:
myapp-web-v1.0.0
myapp-web-v1.1.0
myapp-web-v1.2.0
Benefits:
- Easy rollback
- Clear version history
- Parallel running during transition
Encrypted secrets
Secrets are encrypted at rest and decrypted only during deployment.
Benefits:
- Safe to commit to version control
- Single source of truth
- Standard encryption (AES-256-GCM)
File structure
odysseus-core/
├── lib/odysseus/
│ ├── config/
│ │ └── parser.rb # YAML parsing
│ ├── validators/
│ │ └── config.rb # Configuration validation
│ ├── deployer/
│ │ ├── executor.rb # Main deployment logic
│ │ └── ssh.rb # SSH connection management
│ ├── docker/
│ │ └── client.rb # Docker CLI wrapper
│ ├── caddy/
│ │ └── client.rb # Caddy admin API
│ ├── builder/
│ │ └── client.rb # Image building
│ ├── secrets/
│ │ ├── encrypted_file.rb # Encryption/decryption
│ │ └── loader.rb # Secret loading
│ ├── orchestrator/
│ │ ├── web_deploy.rb # Web role deployment
│ │ ├── job_deploy.rb # Job role deployment
│ │ └── accessory_deploy.rb # Accessory deployment
│ └── host_providers/
│ ├── base.rb # Base class
│ ├── static.rb # Static hosts
│ └── aws_asg.rb # AWS ASG discovery
odysseus-cli/
├── lib/odysseus/cli/
│ └── cli.rb # CLI implementation
└── exe/
└── odysseus # Executable entry point
Extension points
Custom host providers
Implement the base provider interface:
module Odysseus
module HostProviders
class Custom < Base
def resolve
# Return array of hostnames/IPs
end
end
end
end
Custom validators
Add validation rules:
module Odysseus
module Validators
class Custom
def validate(config)
# Return array of error messages
end
end
end
end