Skip to content

unomena/lxc-compose

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

282 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LXC Compose

Docker Compose-like orchestration for LXC containers with a focus on simplicity, security, and minimal footprint.

Why LXC Compose?

The Single-Server Renaissance

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.

The Problem with Container Orchestration Today

  • 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.

The LXC Compose Solution

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.

Real Cost Comparison

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

Perfect For

  • 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

Not Another Docker

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

Migration is Simple

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 section

Most applications can be migrated in under an hour. See our migration guide →

Table of Contents

Features

🚀 Production-Ready Resilience (v2.1+)

  • 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

Core Features

  • 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 .env file support with automatic propagation to all services
  • Container dependencies: Ordered startup with depends_on
  • Post-install automation: Flexible container initialization with environment context

Quick Start

Create a full stack with one command:

# 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.py
lxc-compose up    # That's it! Full stack running

Installation

Quick Install (One-line)

# 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'

Local Installation

git clone https://github.com/unomena/lxc-compose.git
cd lxc-compose
sudo ./install.sh

The install script automatically detects whether it's being run locally or remotely and handles the installation accordingly.

Commands

Core Commands

lxc-compose up

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)

lxc-compose down

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)

lxc-compose list

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-wide

lxc-compose destroy

Stop 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)

Additional Commands

lxc-compose logs

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 log

lxc-compose test

Run 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 datastore

Configuration

GitHub-Based Templates (NEW!)

LXC 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 needed

Using Custom Repositories

You 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

How It Works

  1. Templates are fetched from library/templates/ in the repository
  2. Services are fetched from library/services/{os}/{version}/{service}/
  3. No caching - always fetches the latest version
  4. Fallback - uses local files if GitHub is unavailable

Template Inheritance System

LXC Compose uses a three-layer inheritance model:

Template (Base OS) → Library Services (includes) → Your Config (overrides)

1. Templates - Base Operating System

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.yml

2. Library Services - Pre-configured Applications

Services 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/

3. Your Configuration - Final Customization

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'"

Basic Configuration

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 /app

Advanced Configuration

See 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

Package Installation Retry Configuration

LXC Compose automatically retries package installation when mirrors are slow or unavailable. This is especially useful for Alpine Linux which frequently experiences CDN issues.

Environment Variables

  • 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)

Example Usage

# 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

How It Works

  1. Exponential Backoff: Waits 1, 2, 4, 8, 16... seconds between retries (capped at MAX_BACKOFF)
  2. Mirror Rotation: Automatically tries alternative mirrors when timeouts occur
    • Alpine: 7 different CDN endpoints
    • Ubuntu/Debian: 4 different archive mirrors
  3. Smart Detection: Recognizes timeout and DNS issues to switch mirrors faster
  4. Graceful Degradation: Continues with warning if all retries fail (won't break your deployment)

Service Library

Pre-configured, production-ready services available in the library/ directory:

Databases

  • 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)

Web/Proxy

  • Nginx - Web server/reverse proxy (Alpine-based, 80/443)
  • HAProxy - Load balancer (Alpine-based, 80/443/8404)

Caching

  • Memcached - Distributed memory cache (Alpine-based, 11211)

Message Queues

  • RabbitMQ - Message broker (Ubuntu-based, 5672/15672)

Monitoring/Search

  • 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 up

See library/README.md for detailed service documentation.

Directory Structure

Project Directory

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

System Installation

/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

Sample Projects

Ready-to-use configurations in samples/ directory:

django-celery-app

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://localhost

django-minimal

Simplified 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:8000

flask-app

Flask 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://localhost

nodejs-app

Express.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:3000

Testing

LXC Compose includes a comprehensive testing framework:

Test Types

  1. Internal Tests: Run inside the container to verify services
  2. External Tests: Run from host to verify connectivity
  3. Port Forwarding Tests: Verify iptables DNAT rules

Writing Tests

See docs/TESTING.md for complete testing documentation.

Networking

Container Communication

  • All containers share /srv/lxc-compose/etc/hosts for name resolution
  • Containers can communicate using container names as hostnames
  • Container IPs are tracked in /etc/lxc-compose/container-ips.json

Port Security

  • Only exposed_ports are 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.

Logs

Log Management

  • Define logs in lxc-compose.yml for each container
  • View logs with lxc-compose logs command
  • Follow logs in real-time with --follow flag
  • Logs are automatically discovered from supervisor configs

Example Log Configuration

logs:
  - django:/var/log/django/django.log
  - nginx:/var/log/nginx/access.log
  - postgres:/var/lib/postgresql/logfile

Security

Security Features

  • 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 .env files
  • No hardcoded credentials: All configuration via environment

Best Practices

  • Never expose database ports unless necessary
  • Use .env files for all sensitive configuration
  • Regularly update container packages
  • Monitor logs for suspicious activity

Requirements

System Requirements

  • 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

Network Requirements

  • IP forwarding enabled
  • iptables available
  • Bridge network configured (lxdbr0)

Documentation

Detailed Guides

Quick References

Troubleshooting

Services Not Starting After Container Restart

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.sh to 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 PostgreSQL

Environment Variables Not Available to Services

Problem: 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.sh

Port Forwarding Not Working After Container Recreation

Problem: 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.yml

Supervisor Config Not Found

Problem: 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 reload

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

License

MIT - See LICENSE for details

About

A Docker Compose-like orchestration tool for LXC containers that provides simple, declarative configuration for deploying and managing multi-container applications using Linux Containers (LXC).

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors