Docker Compose-like orchestration for LXC containers with a focus on simplicity, security, and minimal footprint.
In an era where cloud costs are spiraling and Kubernetes complexity is overwhelming, LXC Compose brings back the simplicity of single-server deployments without sacrificing modern DevOps practices.
- Docker's Resource Tax: Docker containers require pre-allocated resources, leading to waste and inefficiency. You're paying for CPU and memory that sits idle.
- Kubernetes Overhead: Running Kubernetes means multiple nodes, control planes, and massive complexity for what might be a simple application.
- Cloud Lock-in: Platforms like AWS Fargate or Google Cloud Run charge premium prices per container, making multi-container architectures expensive.
- Lost Simplicity: What happened to just deploying your app on a server? The simplicity is buried under layers of abstraction.
LXC Compose leverages Linux Containers (LXC) - the same technology that powers Docker - but removes the unnecessary layers:
- System Containers vs Application Containers: LXC runs system containers that behave like lightweight VMs. Multiple services can run in one container, reducing overhead.
- Shared Kernel Efficiency: Containers share the host kernel directly, using resources dynamically as needed - no pre-allocation required.
- Single Server Power: One modern server can efficiently run dozens of containers that would require multiple Docker hosts or Kubernetes nodes.
- Familiar Syntax: If you know Docker Compose, you already know LXC Compose. The same YAML format, the same concepts, just more efficient.
Consider a typical web application with 5 services (web, api, worker, database, cache):
| Platform | Monthly Cost | Notes |
|---|---|---|
| AWS Fargate | ~$200-400 | Pay per container, per CPU/memory allocation |
| Kubernetes (EKS) | ~$150-300 | Minimum 2-3 nodes + control plane costs |
| Docker on EC2 | ~$50-150 | Requires larger instance for resource allocation |
| LXC Compose | ~$20-40 | Single server, dynamic resource sharing |
- Startups: Run your entire stack on one server until you truly need to scale
- Side Projects: Deploy multiple projects on one VPS without interference
- Agencies: Host client applications efficiently without container-per-client costs
- Self-Hosting: Run your own services (GitLab, Nextcloud, etc.) with minimal overhead
- Development Teams: Replicate production on modest hardware
LXC Compose isn't trying to replace Docker for microservices or cloud-native applications. Instead, it's bringing back the server-native deployment model with modern tooling:
- ✅ Use LXC Compose when: You want to run on a single server, minimize costs, and keep things simple
- ❌ Use Docker/K8s when: You need multi-cloud deployment, have true microservices, or require orchestration at scale
Already using Docker Compose? Migration is straightforward:
# Your existing docker-compose.yml concepts map directly:
- image → template + packages
- ports → exposed_ports
- volumes → mounts
- environment → .env file
- command → services sectionMost applications can be migrated in under an hour. See our migration guide →
- Features
- Installation
- Commands
- Configuration
- Service Library
- Directory Structure
- Sample Projects
- Testing
- Networking
- Logs
- Security
- Requirements
- Documentation
- License
- Automatic Service Recovery: Supervisor auto-starts on container restart, bringing all services back online
- Environment Variable Inheritance: Services automatically inherit .env variables through load-env.sh wrapper
- OS-Aware Configuration: Supervisor configs placed correctly for Ubuntu (/etc/supervisor/conf.d/) and Alpine (/etc/supervisor.d/)
- Port Forwarding Persistence: UPF rules automatically update when containers get new IPs after destroy/recreate
- Database Auto-Start: PostgreSQL, MySQL, and other databases configured to start automatically on boot
- "Pull the Plug" Resilience: Complete recovery after system restart - no manual intervention needed
- Resilient Package Installation: Automatic retry with exponential backoff and mirror fallback for unreliable package servers
- Docker Compose-like syntax: Familiar YAML configuration for LXC containers
- Template Inheritance: Start with base OS, add library services, customize on top
- Pre-configured Library: 77 production-ready services (PostgreSQL, Redis, Nginx, etc.) across 7 base images
- Composite Containers: Combine multiple services efficiently in one container
- Minimal footprint: Alpine Linux support for ~150MB containers
- Dynamic service configuration: Define services in YAML, auto-generate supervisor configs with proper OS detection
- Comprehensive testing: Built-in health checks with test inheritance from library services
- Shared networking: Containers communicate via shared hosts file with persistent IPs
- Security-first: iptables rules block all non-exposed ports with proper cleanup on destroy
- Log aggregation: Centralized log viewing with automatic log inheritance
- Environment variables: Full
.envfile support with automatic propagation to all services - Container dependencies: Ordered startup with
depends_on - Post-install automation: Flexible container initialization with environment context
# lxc-compose.yml
version: '1.0'
containers:
mystack:
template: ubuntu-minimal-24.04
includes:
- postgresql # Database
- redis # Cache
- nginx # Web server
packages: [python3, python3-pip]
mounts: [./app:/app]
services:
app:
command: python3 /app/main.pylxc-compose up # That's it! Full stack running# Using curl
curl -fsSL https://raw.githubusercontent.com/unomena/lxc-compose/main/install.sh | sudo bash
# Or using wget
wget -qO- https://raw.githubusercontent.com/unomena/lxc-compose/main/install.sh | sudo bash
# Note: The pipe to 'sudo bash' is important - don't use 'sudo curl'git clone https://github.com/unomena/lxc-compose.git
cd lxc-compose
sudo ./install.shThe install script automatically detects whether it's being run locally or remotely and handles the installation accordingly.
Create and start containers from configuration.
lxc-compose up # Use lxc-compose.yml in current directory
lxc-compose up -f custom.yml # Use custom config file
lxc-compose up --all # Start ALL containers system-wide (requires confirmation)Stop running containers.
lxc-compose down # Stop containers from lxc-compose.yml
lxc-compose down -f custom.yml # Stop containers from custom config
lxc-compose down --all # Stop ALL containers system-wide (requires confirmation)List containers and their status.
lxc-compose list # List containers from lxc-compose.yml
lxc-compose list -f custom.yml # List containers from custom config
lxc-compose list --all # List ALL containers system-wideStop and permanently remove containers.
lxc-compose destroy # Destroy containers from lxc-compose.yml
lxc-compose destroy -f custom.yml # Destroy containers from custom config
lxc-compose destroy --all # Destroy ALL containers (DANGEROUS - requires confirmation)View and follow container logs.
lxc-compose logs <container> # List available logs for container
lxc-compose logs <container> <log-name> # View specific log
lxc-compose logs <container> <log-name> --follow # Follow log in real-time
Examples:
lxc-compose logs sample-django-app # List all available logs
lxc-compose logs sample-django-app django # View Django application log
lxc-compose logs sample-django-app nginx --follow # Follow nginx access logRun health check tests for containers.
lxc-compose test # Test all containers
lxc-compose test <container> # Test specific container
lxc-compose test <container> list # List available tests for container
lxc-compose test <container> internal # Run only internal tests
lxc-compose test <container> external # Run only external tests
lxc-compose test <container> port_forwarding # Run only port forwarding tests
Examples:
lxc-compose test # Run all tests for all containers
lxc-compose test sample-django-app # Run all tests for Django app
lxc-compose test sample-datastore internal # Run internal tests for datastoreLXC Compose now fetches templates and services directly from GitHub, eliminating the need for local installation:
# Templates and services are fetched automatically from GitHub
lxc-compose up # Downloads templates/services as neededYou can use your own fork or custom repository:
# Use a custom repository
export LXC_COMPOSE_REPO=https://github.com/yourusername/lxc-compose
export LXC_COMPOSE_BRANCH=my-custom-branch
lxc-compose up
# Or use a specific version/tag
export LXC_COMPOSE_BRANCH=v1.0.0
lxc-compose up- Templates are fetched from
library/templates/in the repository - Services are fetched from
library/services/{os}/{version}/{service}/ - No caching - always fetches the latest version
- Fallback - uses local files if GitHub is unavailable
LXC Compose uses a three-layer inheritance model:
Template (Base OS) → Library Services (includes) → Your Config (overrides)
Templates define the base OS image and initial configuration:
- Alpine 3.19 - Minimal footprint (~150MB)
- Ubuntu 24.04/22.04 - Full Ubuntu environment
- Ubuntu-minimal 24.04/22.04 - Balanced size/features (~300MB)
- Debian 12/11 - Stable Debian base
Example:
containers:
myapp:
template: alpine-3.19 # Fetched from GitHub: library/templates/alpine-3.19.ymlServices are pre-configured applications that extend templates:
- Databases: PostgreSQL, MySQL, MongoDB
- Caching: Redis, Memcached
- Web: Nginx, HAProxy
- Messaging: RabbitMQ
- Search: Elasticsearch
- Monitoring: Grafana, Prometheus
Example:
containers:
myapp:
template: alpine-3.19
includes:
- nginx # Fetched from: library/services/alpine/3.19/nginx/
- postgresql # Fetched from: library/services/alpine/3.19/postgresql/Your local configuration has the highest priority:
containers:
myapp:
template: alpine-3.19
includes: [nginx]
packages: [curl, jq] # Additional packages
exposed_ports: [8080] # Additional ports
post_install: # Your custom setup
- name: "Setup my app"
command: "echo 'Custom setup here'"version: "1.0"
containers:
myapp:
image: ubuntu:jammy # Base OS image (ubuntu:22.04, images:alpine/3.19, etc.)
packages: # Packages to install
- nginx
- python3
exposed_ports: [80, 443] # Ports accessible from host
mounts: # Directory/file mounts
- ".:/app" # Mount current directory to /appSee docs/CONFIGURATION.md for complete configuration reference including:
- Container templates and releases
- Service definitions
- Environment variables
- Mount configurations
- Post-install commands
- Container dependencies
- Test configurations
- Log definitions
LXC Compose automatically retries package installation when mirrors are slow or unavailable. This is especially useful for Alpine Linux which frequently experiences CDN issues.
LXC_COMPOSE_PKG_RETRIES: Number of retry attempts per mirror (default: 5)LXC_COMPOSE_MAX_BACKOFF: Maximum backoff time in seconds between retries (default: 32)
# Increase retries for unreliable networks
export LXC_COMPOSE_PKG_RETRIES=10
export LXC_COMPOSE_MAX_BACKOFF=60
lxc-compose up
# Or use inline for a single command
LXC_COMPOSE_PKG_RETRIES=3 lxc-compose up- Exponential Backoff: Waits 1, 2, 4, 8, 16... seconds between retries (capped at MAX_BACKOFF)
- Mirror Rotation: Automatically tries alternative mirrors when timeouts occur
- Alpine: 7 different CDN endpoints
- Ubuntu/Debian: 4 different archive mirrors
- Smart Detection: Recognizes timeout and DNS issues to switch mirrors faster
- Graceful Degradation: Continues with warning if all retries fail (won't break your deployment)
Pre-configured, production-ready services available in the library/ directory:
- PostgreSQL - Relational database (Alpine-based, 5432)
- MySQL - Relational database (Ubuntu-based, 3306)
- MongoDB - NoSQL database (Ubuntu-based, 27017)
- Redis - In-memory cache/database (Alpine-based, 6379)
- Nginx - Web server/reverse proxy (Alpine-based, 80/443)
- HAProxy - Load balancer (Alpine-based, 80/443/8404)
- Memcached - Distributed memory cache (Alpine-based, 11211)
- RabbitMQ - Message broker (Ubuntu-based, 5672/15672)
- Elasticsearch - Search and analytics (Ubuntu-based, 9200/9300)
- Grafana - Metrics visualization (Ubuntu-based, 3000)
- Prometheus - Metrics collection (Ubuntu-based, 9090)
Quick deploy any service:
cp -r library/postgresql ~/myproject/
cd ~/myproject/postgresql
lxc-compose upSee library/README.md for detailed service documentation.
project/
├── lxc-compose.yml # Main configuration file
├── .env # Environment variables
├── requirements.txt # Python dependencies (Python projects)
├── package.json # Node dependencies (Node projects)
├── config/ # Configuration files
│ ├── container-name/ # Per-container configs
│ │ ├── supervisord.conf
│ │ ├── nginx.conf
│ │ └── redis.conf
├── tests/ # Test scripts
│ ├── internal_tests.sh # Tests run inside container
│ ├── external_tests.sh # Tests run from host
│ └── port_forwarding_tests.sh # Port forwarding verification
├── src/ # Application source code
└── README.md # Project documentation
/srv/lxc-compose/ # Installation directory
├── cli/
│ └── lxc_compose.py # Main CLI script
├── docs/ # Documentation
├── samples/ # Sample projects
└── etc/
└── hosts # Shared hosts file for containers
/etc/lxc-compose/
└── container-ips.json # Container IP tracking
/var/log/lxc-compose/ # Log directory
Ready-to-use configurations in samples/ directory:
Full-featured Django application with Celery workers, PostgreSQL, and Redis.
- Multi-container setup with proper dependencies
- Alpine datastore (~150MB) + Ubuntu Minimal app (~300MB)
- Nginx reverse proxy
- Supervisor for process management
- Comprehensive test suite
cd samples/django-celery-app
lxc-compose up
# Access at http://localhostSimplified Django + PostgreSQL in a single Alpine container (~150MB).
- Minimal footprint
- PostgreSQL and Django in one container
- Ideal for development
cd samples/django-minimal
lxc-compose up
# Access at http://localhost:8000Flask application with Redis caching.
- Redis for session/cache storage
- Nginx reverse proxy
- Production-ready configuration
cd samples/flask-app
lxc-compose up
# Access at http://localhostExpress.js application with MongoDB.
- MongoDB for data persistence
- PM2 for process management
- API-ready configuration
cd samples/nodejs-app
lxc-compose up
# Access at http://localhost:3000LXC Compose includes a comprehensive testing framework:
- Internal Tests: Run inside the container to verify services
- External Tests: Run from host to verify connectivity
- Port Forwarding Tests: Verify iptables DNAT rules
See docs/TESTING.md for complete testing documentation.
- All containers share
/srv/lxc-compose/etc/hostsfor name resolution - Containers can communicate using container names as hostnames
- Container IPs are tracked in
/etc/lxc-compose/container-ips.json
- Only
exposed_portsare accessible from the host - iptables DNAT rules handle port forwarding
- All other ports are blocked by default FORWARD rules
See docs/NETWORKING.md for detailed networking documentation.
- Define logs in
lxc-compose.ymlfor each container - View logs with
lxc-compose logscommand - Follow logs in real-time with
--followflag - Logs are automatically discovered from supervisor configs
logs:
- django:/var/log/django/django.log
- nginx:/var/log/nginx/access.log
- postgres:/var/lib/postgresql/logfile- Port isolation: Only explicitly exposed ports are accessible
- iptables rules: Automatic firewall configuration
- Container isolation: LXC provides kernel-level isolation
- Environment variables: Sensitive data kept in
.envfiles - No hardcoded credentials: All configuration via environment
- Never expose database ports unless necessary
- Use
.envfiles for all sensitive configuration - Regularly update container packages
- Monitor logs for suspicious activity
- OS: Ubuntu 22.04 or 24.04 LTS
- LXD/LXC: Installed and configured
- Python: 3.8 or higher
- Dependencies: python3-yaml, python3-click
- Privileges: Root/sudo access for container operations
- IP forwarding enabled
- iptables available
- Bridge network configured (lxdbr0)
- Configuration Reference - Complete YAML configuration guide
- Commands Reference - Detailed command documentation
- Testing Guide - Writing and running tests
- Networking Guide - Network configuration and security
- Standards Guide - Project configuration standards
- Troubleshooting - Common issues and solutions
- Migration Guide - Migrating from Docker Compose
- API Reference - Python API for custom integrations
Problem: Supervisor or services don't auto-start after container restart.
Solution: This has been fixed in v2.1+. The fix includes:
- Supervisor is enabled in the init system (systemd for Ubuntu, OpenRC for Alpine)
- Services are wrapped with
/usr/local/bin/load-env.shto inherit environment variables - Database services (PostgreSQL, MySQL) are configured to auto-start
Manual Fix for Existing Containers:
# For Ubuntu containers
lxc exec <container> -- systemctl enable supervisor
# For Alpine containers
lxc exec <container> -- rc-update add supervisord default
lxc exec <container> -- rc-update add postgresql default # If using PostgreSQLProblem: Services can't access variables from .env file.
Solution: Services are now automatically wrapped with an environment loader. The system creates /usr/local/bin/load-env.sh that sources the .env file before executing commands.
Manual Fix:
# Create the environment loader
lxc exec <container> -- bash -c 'cat > /usr/local/bin/load-env.sh << "EOF"
#!/bin/bash
if [ -f /app/.env ]; then
set -a
source /app/.env
set +a
fi
exec "$@"
EOF'
lxc exec <container> -- chmod +x /usr/local/bin/load-env.shProblem: After destroying and recreating containers, port forwarding points to old IPs.
Solution: v2.1+ automatically cleans up and updates UPF rules. The system:
- Removes existing rules before adding new ones
- Uses hostname-based forwarding for resilience
- Properly cleans up rules on container destroy
Manual Fix:
# Clean all UPF rules and let lxc-compose recreate them
sudo upf clean
lxc-compose down -f lxc-compose.yml
lxc-compose up -f lxc-compose.ymlProblem: Supervisor can't find service configurations.
Solution: The system now detects the OS and places configs in the correct location:
- Ubuntu/Debian:
/etc/supervisor/conf.d/*.conf - Alpine:
/etc/supervisor.d/*.ini
Manual Fix:
# For Ubuntu - move configs to correct location
lxc exec <container> -- mv /etc/supervisor.d/*.ini /etc/supervisor/conf.d/
lxc exec <container> -- supervisorctl reloadContributions are welcome! Please see CONTRIBUTING.md for guidelines.
MIT - See LICENSE for details