Deploy infrastructure services (Memcached, Redis, HAProxy, Nginx) to servers with Ruby and SSH. No Ansible, no Python, no Kubernetes.
Works standalone with any Ruby application or integrates seamlessly with Rails.
- Memcached - In-memory caching with configurable parameters
- Redis - Key-value store and cache
- HAProxy - Load balancer with SSL/TLS support
- Nginx - Web server and reverse proxy
- Simple Configuration - YAML files with environment support
- Flexible Overrides - Custom config templates or parameter overrides
- Secret Management - Environment variables or command execution
- SSH Deployment - Direct deployment to Ubuntu servers
- Health Checks - Monitor service status across hosts
- VPN Support - Optional private_ip field for VPN/WireGuard deployments
- Rails Integration - Generators, rake tasks, and seamless integration
- Standalone Support - Works with any Ruby application
gem install boring_services
boringservices setup
boringservices status
# Target a different config/environment
BORING_SERVICES_CONFIG=config/services.staging.yml \
BORING_SERVICES_ENV=staging boringservices setup# Gemfile
gem 'boring_services'# Generate config
rails generate boring_services:install
# Deploy services
rails boring_services:setup
# Deploy using a different config/environment
BORING_SERVICES_CONFIG=config/services.staging.yml BORING_SERVICES_ENV=staging \
rails boring_services:setup
# Check health
rails boring_services:healthCreate config/services.yml:
production:
user: ubuntu
ssh_key: ~/.ssh/id_rsa
forward_agent: true
use_ssh_agent: false
ssh_auth_methods:
- publickey
services:
- name: memcached
enabled: true
hosts:
- host: 10.8.0.20
label: cache-a
- host: 10.8.0.21
label: cache-b
port: 11211
memory_mb: 256
- name: redis
enabled: true
host: 10.8.0.22
port: 6379
memory_mb: 1024
- name: haproxy
enabled: true
hosts:
- 10.8.0.30
port: 80
stats_port: 8404
backends:
- host: 10.8.0.10
port: 3000
- host: 10.8.0.11
port: 3000
secrets:
redis_password: $REDIS_PASSWORDUse host: for a single target or hosts: for multiple VMs. When using hosts, each entry can be a simple hostname/IP or a hash with per-host overrides (e.g., label, port, or memory_mb).
Basic Configuration:
- name: memcached
enabled: true
hosts:
- 10.8.0.20
port: 11211 # Default: 11211
memory_mb: 256 # Memory allocation in MBWith Custom Parameters:
- name: memcached
enabled: true
host: 10.8.0.20
port: 11211
memory_mb: 512
custom_params:
listen_address: 0.0.0.0 # Default: 0.0.0.0
max_connections: 2048 # Default: 1024
max_item_size: 2m # Optional: max item size
verbosity: 2 # Optional: 0-3 for debug outputWith Custom Config Template:
- name: memcached
enabled: true
host: 10.8.0.20
custom_config_template: config/memcached.custom.conf # Path to custom config file- name: redis
enabled: true
hosts:
- 10.8.0.21
port: 6379 # Default: 6379
memory_mb: 512 # Memory allocationOptional password protection via secrets:
secrets:
redis_password: $REDIS_PASSWORDBasic HTTP Load Balancer:
- name: haproxy
enabled: true
host: 10.8.0.30
port: 80 # Frontend port
stats_port: 8404 # Stats dashboard port
backends:
- host: 10.8.0.10
port: 3000
- host: 10.8.0.11
port: 3000HTTPS with SSL Termination:
- name: haproxy
enabled: true
host: 34.123.78.90
port: 80 # Redirects to HTTPS
https_port: 443 # SSL/TLS port
stats_port: 8404
ssl: true
ssl_cert: $(op read "op://MyVault/haproxy-ssl/certificate")
ssl_key: $(op read "op://MyVault/haproxy-ssl/private_key")
backends:
- host: 192.168.1.10
port: 3000
- host: 192.168.1.11
port: 3000With Custom Parameters:
- name: haproxy
enabled: true
host: 10.8.0.30
port: 80
stats_port: 8404
custom_params:
timeout_connect: 10000 # Default: 5000ms
timeout_client: 60000 # Default: 50000ms
timeout_server: 60000 # Default: 50000ms
balance: leastconn # Default: roundrobin
health_check_path: /healthz # Default: /health
ssl_ciphers: CUSTOM_CIPHERS # Custom SSL cipher suite
ssl_options: no-sslv3 # Custom SSL options
backends:
- host: 10.8.0.10
port: 3000With Custom Config Template:
- name: haproxy
enabled: true
host: 10.8.0.30
custom_config_template: config/haproxy.custom.cfg # Path to custom HAProxy config
# Note: When using custom template, most other options are ignoredSSL Certificate Options:
# Option 1: 1Password CLI
ssl_cert: $(op read "op://MyVault/haproxy-ssl/certificate")
ssl_key: $(op read "op://MyVault/haproxy-ssl/private_key")
# Option 2: Environment variables
ssl_cert: $HAPROXY_SSL_CERT
ssl_key: $HAPROXY_SSL_KEY
# Option 3: Rails credentials (Rails only)
ssl_cert: $(rails credentials:show | yq .haproxy.ssl.cert)
ssl_key: $(rails credentials:show | yq .haproxy.ssl.key)
# Option 4: Local files
ssl_cert: $(cat /path/to/cert.pem)
ssl_key: $(cat /path/to/key.pem)HAProxy Features:
- ✅ Config Validation - Automatically validates HAProxy config before applying
- ✅ SSL/TLS Support - Automatic HTTPS setup with certificate management
- ✅ Self-Signed Fallback - Generates self-signed cert if none provided
- ✅ HTTP → HTTPS Redirect - Automatic redirect when SSL is enabled
- ✅ Health Checks - Configurable health check endpoint (default: GET /health)
- ✅ Stats Dashboard - Access at
http://your-host:8404/ - ✅ Custom Overrides - Override timeouts, balance algorithms, SSL ciphers
- ✅ Custom Templates - Bring your own HAProxy config file
- name: nginx
enabled: true
hosts:
- 10.8.0.40
port: 80
ssl: true # Enable HTTPS on port 443
backends:
- host: 10.8.0.10
port: 3000boringservices install
boringservices install redis
boringservices install -e stagingboringservices status
boringservices status -e productionboringservices restart redis
boringservices restart haproxy -e stagingboringservices uninstall memcachedAdd to your Gemfile:
gem 'boring_services'Generate configuration:
rails generate boring_services:install
# Creates config/services.yml
rails boring_services:setup
# Deploys all enabled servicesUse services in your Rails app:
Rails.application.configure do
# Connect to Memcached
config.cache_store = :mem_cache_store, '10.8.0.20:11211', '10.8.0.21:11211'
# Connect to Redis for sessions
config.session_store :redis_store,
servers: ['redis://10.8.0.22:6379/0/session'],
expire_after: 90.minutes
endAvailable Rake Tasks:
rails boring_services:setup # Deploy all services
rails boring_services:health # Check service health
rails boring_services:status # Show service status
rails boring_services:restart # Restart all servicesUse environment variables or command execution:
secrets:
# Option 1: Environment variable
redis_password: $REDIS_PASSWORD
# Option 2: 1Password CLI
redis_password: $(op read "op://MyVault/redis/password")
# Option 3: Rails credentials (Rails only)
redis_password: $(rails credentials:show | yq .redis.password)
# Option 4: File-based secrets
redis_password: $(cat .secrets/redis_password)BoringServices supports deploying services over VPN or private networks (e.g., WireGuard, Tailscale):
- Public IPs (
host) for SSH deployment access - Private IPs (
private_ip) for service communication - Services listen on all interfaces (0.0.0.0) by default
- Accessible via private network IPs
- No public exposure of services
Example with Private IPs:
services:
- name: redis
host: 18.234.67.89 # ← Public IP for SSH deployment
private_ip: 10.8.0.32 # ← Private VPN IP for connections
label: us-east-1
port: 6379
memory_mb: 1024
- name: memcached
host: 51.15.214.103 # ← Public IP for SSH
private_ip: 10.8.0.61 # ← Private VPN IP
label: cache-eu
port: 11211
memory_mb: 256Your application connects via the private IP:
# Ruby/Rails app
Redis.new(url: 'redis://10.8.0.32:6379')
# Memcached
Dalli::Client.new('10.8.0.61:11211')The private_ip field is optional and purely for documentation - use it to track which IPs your application should connect to.
boringservices statusOutput:
memcached: healthy
✓ 10.8.0.20: running
✓ 10.8.0.21: running
redis: healthy
✓ 10.8.0.22: running
haproxy: healthy
✓ 10.8.0.30: running
- Ruby 3.0+
- Ubuntu 20.04+ servers (Debian-based distributions)
- SSH key-based authentication
- Optional: VPN solution (WireGuard, Tailscale, etc.) for private networking
Standalone Ruby:
gem install boring_servicesWith Bundler:
# Gemfile
gem 'boring_services'Then run:
bundle installFor Rails projects, the gem will automatically integrate with Rails and provide generators and rake tasks.
MIT