π Deploy multiple Docker Compose apps effortlessly on a single Ubuntu server
Perfect for rapid prototyping, staging environments, and cost-effective hosting without complex CI/CD infrastructure. While designed for experiments and staging, it can be used for production but it's not recommended.
π‘ The Problem: Setting up individual hosting, CI/CD pipelines, and SSL certificates for every experiment or staging app is expensive and time-consuming. Why reinvent the wheel every time?
β The Solution: One server, one setup, unlimited apps with automatic HTTPS and deployments.
- ποΈ Quick Experiments β Deploy any Docker Compose app in under 5 minutes
- π° Cost Effective β Host multiple apps on one server instead of paying for separate hosting/serverless
- π Automatic HTTPS β Let's Encrypt certificates managed automatically via Traefik
- π Auto-Deploy β Git-based deployments without webhooks or complex CI/CD
- π οΈ Simple Management β Powerful CLI for app lifecycle and debugging
- π¦ No Vendor Lock-in β Standard Docker Compose, runs anywhere
- Staging environments for multiple projects
- Personal projects and side experiments
- Small team demos and proof-of-concepts
- Development previews from feature branches
- Simple production apps (where scaling is not needed)
- One Traefik instance handles reverse proxy and SSL for all apps
- Each app lives in its own Git repo with a standard Docker Compose file
- Systemd timers poll for changes and auto-deploy (no webhooks needed)
- Simple CLI manages everything: create, deploy, debug, monitor
Run on a fresh Ubuntu 22.04+ server as root, or to update an existing installation:
bash -c "$(curl -fsSL https://raw.githubusercontent.com/r-Larch/multi-deploy/refs/heads/master/setup.sh)"
The installer will:
- Install Docker (if missing)
- Download this repository into
/opt/multi-deploy
- Ask for your Let's Encrypt email for Traefik and start Traefik on ports 80/443
- Install systemd units for auto-deploy timers
- Add
/opt/multi-deploy/bin
to PATH (new shells)
# Interactively create a new app from any Docker Compose repo (private SSH repos supported)
sudo app create myapp https://github.com/user/my-compose-repo.git
# The setup will show you a deploy key if using SSH - add it to your repo's settings
# The create command helps you prepare a compose override file so you can make adjustments,
# like configuring your production app to run in a staging environment
# Configure or override the Traefik router and middleware for your app
# (if not already configured in your repo)
labels:
- "traefik.enable=true"
# You can also add just this line to override the domain if your repo already has valid Traefik config
- "traefik.http.routers.myapp.rule=Host(`myapp-domain.com`)"
- "traefik.http.routers.myapp.entrypoints=websecure"
- "traefik.http.routers.myapp.tls.certresolver=letsencrypt"
# After configuring, deploy:
sudo app deploy myapp
# Your app is now live at https://myapp-domain.com
# (Optional) Enable auto-deployment (polls every minute for changes)
sudo app timers myapp on
# Manual deployment (pull latest changes and deploy):
sudo app update myapp
What happens behind the scenes:
- Repo cloned to
/opt/multi-deploy/apps/myapp/code
- Generated
compose.yml
usesinclude:
to include your repo's compose files + Traefik config - Systemd timer starts polling for git changes and auto-deploying
Your app automatically deploys on git pushes. No webhooks, no CI/CD setup required.
The app
command provides everything you need to manage your deployments:
# List all apps and their status
sudo app list
# Get detailed info about an app (shows services, containers, git status)
sudo app detail myapp
# View logs (last 50 lines, follows by default)
sudo app logs myapp
# Manual deployment (useful for testing)
sudo app deploy myapp
# Start/stop/restart services
sudo app up myapp
sudo app down myapp
sudo app restart myapp
# Access running containers
sudo app shell myapp [service-name] # Interactive shell
sudo app run myapp [service-name] command # Run one-off commands
# Git operations (useful for branch deployments)
sudo app pull myapp # Pull latest changes
sudo app git myapp status # Check git status
sudo app git myapp switch feature-branch # Deploy a different branch
# View auto-deploy status for an app
sudo app timers myapp
# Control auto-deployment
sudo app timers myapp on # Start auto-deployment timer
sudo app timers myapp off # Stop auto-deployment (app keeps running)
# Complete removal
sudo app delete myapp # Stops services, removes files, disables timer
Having issues? The app
command has built-in debugging tools to help you identify problems quickly.
# See all apps and their current status
sudo app list
# Get detailed information about a specific app
sudo app detail myapp
# Shows: git status, running containers, service states, recent deployments
# Check if auto-deployment is working
sudo app timers myapp
# Manually trigger deployment to see errors
sudo app deploy myapp
# Check git status for your app
sudo app git myapp status
# Verify repo is clean and on the right branch
# Switch to a different branch if needed
sudo app git myapp switch main
# Check if containers are running
sudo app list
# View real-time logs
sudo app logs myapp
# Add service name for specific container: sudo app logs myapp web
# Check the merged compose config:
sudo app detail myapp
# Access container for debugging
sudo app shell myapp web
# Or run specific commands:
sudo app run myapp ls # same as: docker compose -f <app-compose-file> ls
sudo app run myapp kill # same as: docker compose -f <app-compose-file> kill
# Restart if containers are stuck
sudo app restart myapp
App not accessible from web:
- Check Traefik labels in your
compose.yml
orcompose.server.yml
- Verify domain DNS points to your server
- Enable and Check Traefik dashboard eg.
https://traefik.yourdomain.com
Deployment not happening automatically:
- Run
sudo app timers
to verify timer is enabled - Check timer logs:
journalctl -u [email protected]
- Verify git repo is accessible and has changes
Container won't start:
- Check logs:
sudo app logs myapp
- Try manual deployment:
sudo app deploy myapp
- Access container for debugging:
sudo app shell myapp service-name
Permission issues:
- All
app
commands should be run withsudo
- Check file ownership in
/opt/multi-deploy/apps/myapp/
/opt/multi-deploy/
βββ bin/ # CLI tools (app, app-compose, app-git, app-deploy)
βββ apps/ # Your deployed applications
β βββ myapp/
β βββ code/ # Your git repo
β βββ compose.yml # Generated compose file
β βββ compose.server.yml # Traefik configuration
β βββ app.env # App-specific environment
βββ traefik/ # Traefik reverse proxy config
βββ etc/systemd/ # Systemd timer templates
- Systemd timer (every minute) runs
watch-and-deploy.sh
- Git check: Fetch latest commits from origin
- Change detection: Compare local vs remote branch
- Build & deploy: If changes found, run
docker compose build --pull && up -d
- Logging: All output saved to
/opt/multi-deploy/apps/<name>/deploy.log
(rotated weekly)
- External network
web
: Shared between all apps for Traefik routing - Traefik: Handles SSL termination, routing, and Let's Encrypt certificates
- Per-app networks: Each app gets isolated internal networking via Docker Compose
Your app repo can contain:
compose.yml
ordocker-compose.yml
- Main application definition- Multi-deploy will include both files when building the final stack
Set app-specific variables in /opt/multi-deploy/apps/<name>/app.env
:
# Edit app environment
sudo nano /opt/multi-deploy/apps/myapp/app.env
# Restart to apply changes
sudo app restart myapp
Deploy different branches for staging/testing:
# Switch to feature branch
sudo app git myapp switch feature-xyz
# Deploy the branch
sudo app deploy myapp
# Switch back to main when done
sudo app git myapp switch main
Enable the Traefik dashboard for monitoring and debugging your reverse proxy:
# Navigate to Traefik directory
cd /opt/multi-deploy/traefik
# Edit the environment file
sudo nano .env
Update the dashboard settings:
TRAEFIK_DASHBOARD_ENABLE=true
TRAEFIK_DASHBOARD_HOST=my-domain.com
Create dashboard user credentials:
# Add a user to the dashboard (replace with your username/password)
echo "$(htpasswd -nb myusername MyPassw0rd123)" | sudo tee -a dashboard_users
# Restart Traefik to apply changes
sudo docker compose restart
Your Traefik dashboard will be available at https://my-domain.com
with basic authentication using the credentials you created.
Disable auto-deployment for manual control:
# Stop auto-deployment but keep app running
sudo app timers myapp off
# Deploy manually when ready
sudo app deploy myapp
create
- Interactive setup wizard for new appslist
- List all apps with status (running containers, auto-deploy state)detail <name> [service]
- Detailed app info: git status, services, containersdelete <name>
- Remove app completely (stops containers, deletes files)timers <name>
- Show auto-deploy status for an apptimers <name> on
- Start auto-deployment timer (polls every minute)timers <name> off
- Stop auto-deployment timer (app keeps running)
deploy <name>
- Manual deployment: build and restart containersup <name>
- Start all services withdocker compose up -d
down <name>
- Stop all services withdocker compose down
restart <name>
- Restart all services
logs <name> [service]
- View logs (follows by default, last 200 lines)shell <name> <service>
- Open interactive shell in running containerrun <name> <service> command
- Run one-off command in service container
All git operations maintain app context and work within the app's code directory:
pull <name>
- Git fetch + hard reset to origin/branchgit <name> status
- Show git status (branch, commits ahead/behind)git <name> fetch
- Fetch latest commitsgit <name> pull
- Fetch + hard reset to origin/branchgit <name> reset-hard
- Hard reset to origin/branchgit <name> switch <branch>
- Switch and track origin/branch
# Quick health check
sudo app list
sudo app detail myapp
# Manage deployments
sudo app timers myapp on # Start auto-deployment
sudo app deploy myapp # Manual deployment
sudo app timers myapp off # Stop auto-deployment
# Debug issues
sudo app logs myapp web # View specific service logs
sudo app shell myapp web # Access container shell
sudo app git myapp status # Check git state
# Branch deployments
sudo app git myapp switch staging # Deploy staging branch
sudo app deploy myapp # Deploy the new branch
sudo app git myapp switch main # Back to main branch
- OS: Ubuntu 22.04+ (other Debian-based distros may work)
- Memory: 2GB+ recommended (depends on your apps)
- Storage: 20GB+ for system + your app containers and logs
- Network: Public IP with ports 80/443 accessible
- Domain: DNS pointing to your server for automatic HTTPS
-
Public repos: HTTPS or SSH both work fine
-
Private repos: SSH recommended (setup generates SSH key automatically)
-
SSH setup: Ensure your shell has ssh-agent and key loaded if needed:
eval "$(ssh-agent -s)" ssh-add /root/.ssh/id_ed25519
- Don't publish ports on app services; Traefik connects over the shared
web
network - In
compose.server.yml
, only the web service needstraefik.enable=true
and to join theweb
network - Put additional Traefik labels in your app repo compose files if needed (middlewares, routers, etc.)
This project is designed for simplicity and experimentation. Contributions welcome!
- Issues: Report bugs or request features via GitHub Issues
- Pull Requests: Improvements to scripts, documentation, or examples
- Community: Share your multi-deploy setups and use cases
Remember: This tool prioritizes simplicity over complexity. It's perfect for experiments and staging, but evaluate carefully for production workloads requiring high availability or complex orchestration.