Zero-downtime deployments and automatic HTTPS on your own servers.
β― luma deploy --force
Using Git SHA for release ID: 80d0f8c
Starting deployment with release 80d0f8c
[β] Configuration loaded (1ms)
[β] Git status verified (4ms)
[β] Infrastructure ready (879ms)
[β] web β web:80d0f8c (1.8s)
[β] Building Images (1.8s)
[β] App State Reconciliation (884ms)
ββ web β 157.180.25.101
ββ [β] Loading web image (1.7s)
ββ [β] Zero-downtime deployment of web (3.5s)
ββ [β] Configuring proxy for web (805ms)
[β] Deploying Apps (6.2s)
[β] Deployment completed successfully in 9.8s
Your app is live at:
ββ https://test.eliasson.me
Luma automatically handles:
- β Zero-downtime blue-green deployments
- β Automatic SSL certificates via Let's Encrypt
- β Health checks and automatic rollbacks
- β Docker image building and secure transfer
- β Multi-server deployments
npm install -g @elitan/luma
cd your-project
luma init
This creates:
luma.yml
- Your deployment configuration.luma/secrets
- Secure credentials (add to.gitignore
)
Edit luma.yml
:
name: my-app
apps:
web:
image: my-app/web
servers:
- your-server.com
build:
context: .
dockerfile: Dockerfile
proxy:
hosts:
- myapp.com
app_port: 3000
luma setup
Luma will:
- Install Docker if needed
- Set up the reverse proxy
- Start services
luma deploy
Watch as Luma builds, deploys, and switches traffic with zero downtime.
- Local machine: Bun or Node.js 18+
- Target servers:
- Ubuntu/Debian Linux
- SSH access with sudo privileges
- Ports 80 and 443 open
- TypeScript/Bun instead of Ruby
- Registry-less deployments - no need for external Docker registries
- Your own servers - full control, no vendor lock-in
- Any Docker app - not limited to specific frameworks
- Cost-effective - pay only for your servers
- No cold starts - your containers are always running
- Zero-downtime deployments - compose restarts cause downtime
- Multi-server support - deploy across multiple machines
- Automatic SSL and reverse proxy included
- Git-based releases and rollback capabilities
Apps are your main applications (web servers, APIs) that you build and deploy with zero-downtime:
apps:
web:
image: my-app/web
build:
context: .
dockerfile: Dockerfile
proxy:
hosts:
- example.com
app_port: 3000
api:
image: my-app/api
build:
context: ./api
proxy:
hosts:
- api.example.com
app_port: 8080
Services are supporting infrastructure (databases, caches) that use pre-built images:
services:
postgres:
image: postgres:15
environment:
secret: [POSTGRES_PASSWORD]
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7
Luma automatically uses blue-green deployment for apps:
- Build and transfer your Docker image securely to servers
- Deploy new version alongside the current one
- Health check the new version via configured health endpoint
- Switch traffic atomically (sub-millisecond)
- Clean up old version
No Docker registry required - images are transferred directly!
Built apps are transferred using secure Docker save/load:
- Build locally - your Docker images are built on your machine
- Transfer securely - images are saved as tar archives and uploaded via SSH
- Load remotely - Docker loads the image directly on your servers
- No registry needed - eliminates external dependencies and credentials
Services can still use registries for pre-built images like postgres:15
.
Luma includes a smart reverse proxy that automatically:
- Obtains SSL certificates via Let's Encrypt
- Routes traffic to your apps
- Handles health checks
- Manages zero-downtime deployments with blue-green switching
Luma performs automatic health checks during deployments to ensure zero-downtime deployments. Your application must implement a health check endpoint that returns HTTP 200 when healthy.
The default health check endpoint is /up
but you can configure it to any path you want.
apps:
api:
health_check:
path: /health # Health check endpoint (default: /up)
Health Check Behavior (Automatic):
- Method: HTTP GET request
- Success criteria: HTTP 200 response
- Timeout: 5 seconds
- Retries: 3 attempts
- Port: Uses the configured
app_port
(defaults to 80)
The only configurable option is:
- path: Health check endpoint path (default:
/up
)
All other health check behavior is handled automatically by Luma to ensure reliable deployments.
name: my-project # Required: Project name
# Optional: Global settings
ssh:
username: deploy # SSH user (default: root)
port: 22 # SSH port
# Optional: Docker registry (only needed for services using private registries)
docker:
registry: ghcr.io # Docker registry
username: myuser # Registry username
apps:
web:
image: my-app/web # Docker image name
servers:
- server1.com
- server2.com # Target servers
# Build configuration (apps are built locally and transferred)
build:
context: .
dockerfile: Dockerfile
platform: linux/amd64
# Proxy configuration for web apps
proxy:
hosts:
- example.com
- www.example.com
app_port: 3000
# Environment variables
environment:
plain:
- NODE_ENV=production
- PORT=3000
secret:
- DATABASE_URL # From .luma/secrets
- API_KEY
# Health check (optional)
health_check:
path: /health # Health check endpoint (default: /up)
services:
database:
image: postgres:15
servers:
- db.example.com
environment:
secret:
- POSTGRES_PASSWORD
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
Store sensitive values in .luma/secrets
:
# .luma/secrets
DATABASE_URL=postgres://user:pass@localhost:5432/myapp
API_KEY=supersecretkey
# Only needed if using private registries for services
DOCKER_REGISTRY_PASSWORD=myregistrypassword
Important: Add .luma/secrets
to your .gitignore
!
Initialize a new project with configuration files.
Prepare servers for deployment. Installs Docker, creates networks, sets up the proxy.
luma setup # Set up all servers
luma setup web api # Set up only servers for web and api
Deploy apps or services with zero downtime.
luma deploy # Deploy all apps
luma deploy web # Deploy specific app
luma deploy --services # Deploy services instead
luma deploy --force # Skip git status checks
luma deploy --verbose # Detailed logging
Check the status of your deployments.
luma status # Status of all apps
luma status web # Status of specific app
# Example output:
π± App: web
Status: β
RUNNING (green active)
Replicas: 2/2 running
Servers: server1.com, server2.com
name: blog
apps:
web:
image: my-blog
servers:
- server.com
build:
context: .
proxy:
hosts:
- blog.com
app_port: 3000
environment:
secret: [DATABASE_URL]
name: ecommerce
apps:
web:
image: ecommerce/frontend
servers:
- web1.com
- web2.com
build:
context: ./frontend
proxy:
hosts:
- shop.com
- www.shop.com
app_port: 3000
api:
image: ecommerce/backend
servers:
- api.com
build:
context: ./backend
proxy:
hosts:
- api.shop.com
app_port: 8080
environment:
secret: [DATABASE_URL, JWT_SECRET]
services:
postgres:
image: postgres:15
servers:
- db.com
environment:
secret: [POSTGRES_PASSWORD]
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7
servers:
- cache.com
name: platform
apps:
user-service:
image: platform/users
replicas: 3
servers:
- app1.com
- app2.com
build:
context: ./services/users
proxy:
hosts:
- users.platform.com
app_port: 8080
order-service:
image: platform/orders
replicas: 2
servers:
- app1.com
- app2.com
build:
context: ./services/orders
proxy:
hosts:
- orders.platform.com
app_port: 8081
Follow these steps to securely prepare your server for Luma deployments. All commands should be run as the root
user (or with sudo
).
- Create a dedicated user for Luma:
sudo adduser luma
# Follow the prompts to set a password (it will be disabled later) and user details.
sudo usermod -aG sudo luma # Add luma to the sudo group
- Install Docker Engine:
Luma requires Docker to be installed on your target servers.
# Update package lists
sudo apt-get update
# Install prerequisites
sudo apt-get install -y ca-certificates curl
# Create keyrings directory
sudo install -m 0755 -d /etc/apt/keyrings
# Add Docker's official GPG key
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add Docker's stable repository
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Update package lists again
sudo apt-get update
# Install Docker CE with all plugins
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Add the luma user to the docker group
sudo usermod -aG docker luma
# Verify installation
sudo docker run hello-world
Note: You might need to log out and log back in as the luma
user for the group changes to take effect, or use newgrp docker
in the luma
user's session.
-
Set up SSH Key-Based Authentication for
luma
:Prerequisites: This step assumes you already have SSH keys generated on your local machine. If you don't have SSH keys yet, generate them first:
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
# Follow the prompts. It's recommended to use a passphrase for your key.
Copy your public key to the server:
Replace your-server.com
with your server's IP address or hostname.
ssh-copy-id luma@your-server.com
This command will copy your local machine's public SSH key to the luma
user's ~/.ssh/authorized_keys
file on the server.
- Secure SSH Configuration:
Disable password authentication and make other security improvements to your SSH server. Edit the SSH daemon configuration file (/etc/ssh/sshd_config
) on the server:
sudo nano /etc/ssh/sshd_config
Make the following changes:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
UsePAM no # If you are sure you don't need PAM for SSH
After 8000 saving the changes, restart the SSH service:
sudo systemctl restart ssh
Important: Ensure your SSH key authentication is working correctly before disabling password authentication, or you could lock yourself out of the server. Test by logging in from a new terminal window: ssh luma@your-server.com
.
- Install Fail2Ban:
Fail2Ban helps protect your server from brute-force attacks.
sudo apt-get update
sudo apt-get install -y fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Fail2Ban works out-of-the-box with default settings for SSH. You can customize its configuration in /etc/fail2ban/jail.local
.
- Configure Luma:
In your luma.yml
file, ensure you specify the luma
user for SSH connections:
ssh:
username: luma
# port: 22 # If you changed the SSH port
After completing these steps, your server will be more secure and ready for Luma deployments.
- Never commit
.luma/secrets
to version control - Use environment-specific secrets
# luma.staging.yml
name: myapp-staging
apps:
web:
image: myapp/web
build:
context: .
servers:
- staging.myapp.com
# luma.production.yml
name: myapp-prod
apps:
web:
image: myapp/web
build:
context: .
servers:
- prod1.myapp.com
- prod2.myapp.com
Deploy with:
luma deploy -c luma.staging.yml
luma deploy -c luma.production.yml
For services that need private registries or when you prefer registry-based workflows:
# Global registry configuration
docker:
registry: my-registry.com
username: myuser
services:
private-service:
image: my-registry.com/private-service:latest
# Uses global registry config
apps:
web:
# Per-app registry override
registry:
url: ghcr.io
username: different-user
password_secret: GITHUB_TOKEN
# Still uses build + transfer, registry only for pre-built images
git clone https://github.com/elitan/luma
cd luma
bun install
bun run dev
bun test
MIT License
Made with β€οΈ for developers who want simple, reliable deployments on their own infrastructure.