Deploy web applications to VPS using Docker Compose with automatic SSL
Project description
DockLift
Deploy web applications to VPS using Docker Compose with automatic SSL via Caddy.
Features
- Simple Configuration: Lightweight YAML configuration for all deployment settings
- Automatic SSL: Automatic HTTPS certificates via Caddy's ACME integration
- Multi-App Support: Deploy multiple applications to the same VPS, each with its own domain
- Auto Port Assignment: Automatically assigns ports to applications (starts at 3000, increments)
- Idempotent Operations: All commands can be run multiple times safely
- Dependency Management: Built-in support for databases, caches, and other services
- Type-Safe: Full type hints throughout the codebase
- Beautiful CLI: Rich terminal output with progress indicators
Installation
Using UV (recommended):
uv tool install docklift
Using pip:
pip install docklift
Quick Start
1. Initialize Configuration
Run the interactive setup:
docklift init
Or provide values as arguments to skip prompts:
docklift init myapp --domain myapp.example.com --host 192.168.1.100
This creates a docklift.yml file. The wizard will prompt for:
- Application name and domain
- VPS host and SSH credentials
- Application port
- Email for SSL certificate notifications (optional)
2. Bootstrap VPS
First time setup - installs Docker, Caddy, and creates shared infrastructure:
docklift bootstrap
This step is idempotent. It will:
- Install Docker and Docker Compose (if not present)
- Create a shared network for all applications
- Set up Caddy reverse proxy with automatic HTTPS
3. Deploy Application
docklift deploy
This will:
- Upload your application code
- Generate docker-compose.yml
- Build the Docker image on the VPS
- Start the application
- Configure Caddy to route traffic
- Test the deployment
Configuration
Create a docklift.yml file:
vps:
host: 192.168.1.100
user: root
ssh_key_path: ~/.ssh/id_rsa
port: 22
email: admin@example.com # Optional: for Let's Encrypt SSL notifications
application:
name: myapp
domain: myapp.example.com
dockerfile: ./Dockerfile
context: .
# port: 3000 # Optional: auto-assigned if not specified
environment:
NODE_ENV: production
DATABASE_URL: postgres://postgres:password@postgres:5432/myapp
REDIS_URL: redis://redis:6379
dependencies:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: postgres
POSTGRES_PASSWORD: changeme
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
Commands
Initialize
Create a new configuration file interactively:
docklift init
Or provide arguments to skip prompts:
docklift init myapp --domain app.example.com --host 192.168.1.100 --user deploy
Bootstrap
Set up VPS infrastructure (run once per VPS):
docklift bootstrap [--config docklift.yml]
Deploy
Deploy or update an application:
docklift deploy [--config docklift.yml] [--skip-bootstrap]
Status
Check application status:
docklift status [--config docklift.yml]
Remove
Remove an application:
docklift remove [--config docklift.yml] [--remove-volumes]
Environment Variables and Secrets
DockLift supports loading environment variables from .env files to keep secrets out of version control.
Using .env Files
- Create a
.envfile in your project:
# .env
DATABASE_PASSWORD=super-secret-password
API_KEY=sk-1234567890
STRIPE_SECRET=sk_test_abc123
- Reference it in
docklift.yml:
application:
environment:
NODE_ENV: production
DATABASE_URL: postgres://user:${DATABASE_PASSWORD}@postgres:5432/db
env_file: .env # Load additional variables from .env
- Add
.envto.gitignore:
echo ".env" >> .gitignore
Precedence Rules
- Variables defined in
docklift.ymltake precedence over.envfile - This allows you to override secrets for specific environments
- Use YAML for non-sensitive config,
.envfor secrets
Example
With this setup:
# docklift.yml
environment:
NODE_ENV: production
API_KEY: override-key
env_file: .env
# .env
API_KEY=secret-from-env
DATABASE_URL=postgres://localhost/db
The result will be:
NODE_ENV=production(from YAML)API_KEY=override-key(YAML overrides .env)DATABASE_URL=postgres://localhost/db(from .env)
How It Works
Architecture
DockLift creates the following structure on your VPS:
/opt/docklift/
├── caddy-compose.yml # Caddy reverse proxy
├── Caddyfile # Caddy configuration
└── apps/
├── app1/
│ ├── docker-compose.yml
│ └── [app files]
└── app2/
├── docker-compose.yml
└── [app files]
Network Architecture
- All applications and Caddy share a Docker network called
docklift-network - Caddy listens on ports 80 and 443
- Applications expose their internal ports only to the shared network
- Caddy routes traffic based on domain name to the appropriate application
SSL Certificates
- Caddy automatically obtains SSL certificates via Let's Encrypt
- Certificates are automatically renewed
- HTTP traffic is automatically redirected to HTTPS
Port Management
- Applications can specify a port or leave it blank for auto-assignment
- Auto-assigned ports start at 3000 and increment for each new application
- Ports are only exposed internally to the Docker network (no host port conflicts)
- When redeploying an existing app, the same port is reused
Example: Django Application
vps:
host: myserver.example.com
user: deploy
ssh_key_path: ~/.ssh/deploy_key
port: 22
email: admin@myserver.example.com
application:
name: django-app
domain: app.example.com
dockerfile: ./Dockerfile
context: .
port: 8000
environment:
DJANGO_SETTINGS_MODULE: myapp.settings.production
DATABASE_URL: postgres://django:secure_pass@postgres:5432/django_db
REDIS_URL: redis://redis:6379/0
SECRET_KEY: your-secret-key-here
dependencies:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: django_db
POSTGRES_USER: django
POSTGRES_PASSWORD: secure_pass
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
Example: Node.js Application
vps:
host: myserver.example.com
user: deploy
ssh_key_path: ~/.ssh/deploy_key
port: 22
email: admin@myserver.example.com
application:
name: nodejs-app
domain: app.example.com
dockerfile: ./Dockerfile
context: .
port: 3000
environment:
NODE_ENV: production
MONGODB_URL: mongodb://mongo:27017/myapp
dependencies:
mongo:
image: mongo:7
volumes:
- mongo_data:/data/db
Requirements
- Python 3.12+
- SSH access to a VPS running a modern Linux distribution
- Docker-compatible VPS (Ubuntu 20.04+, Debian 11+, etc.)
- Domain name(s) pointed to your VPS
Development
Built with:
- Python 3.12+ with type hints
- UV for package management
- Fabric for SSH operations
- Rich for beautiful CLI output
- Pydantic for configuration validation
- Click for CLI framework
Setup Development Environment
# Clone the repository
git clone https://github.com/amirkarimi/docklift.git
cd docklift
# Install dependencies
uv sync
# Run in development mode
uv run docklift --help
License
MIT
Contributing
Contributions are welcome! Please open an issue or submit a pull request.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file docklift-0.1.1.tar.gz.
File metadata
- Download URL: docklift-0.1.1.tar.gz
- Upload date:
- Size: 60.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a983e0f44592a3df296a6a623db44ca9105697900821f23a70e779bddee86453
|
|
| MD5 |
0288b1eca807afdb78ab33e34baaa70d
|
|
| BLAKE2b-256 |
a47c15fef8a5eb1a4e89192f041fe03c390008757a0b808e2ec65fd4e79059ff
|
Provenance
The following attestation bundles were made for docklift-0.1.1.tar.gz:
Publisher:
publish.yml on amirkarimi/docklift
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
docklift-0.1.1.tar.gz -
Subject digest:
a983e0f44592a3df296a6a623db44ca9105697900821f23a70e779bddee86453 - Sigstore transparency entry: 977777835
- Sigstore integration time:
-
Permalink:
amirkarimi/docklift@a25649651c38730c433ee61f858317944008024b -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/amirkarimi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a25649651c38730c433ee61f858317944008024b -
Trigger Event:
release
-
Statement type:
File details
Details for the file docklift-0.1.1-py3-none-any.whl.
File metadata
- Download URL: docklift-0.1.1-py3-none-any.whl
- Upload date:
- Size: 16.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ae88d32c25e7885d1f6ea11ff362d5f3dcfa276d479221976a6a65ac22fb75f9
|
|
| MD5 |
da20f11aad748680d7fbc5bc624c6051
|
|
| BLAKE2b-256 |
0059fa9fc74199f32400751eaeb61a9184ce93954057ccbd5ab562bd8cb00b0d
|
Provenance
The following attestation bundles were made for docklift-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on amirkarimi/docklift
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
docklift-0.1.1-py3-none-any.whl -
Subject digest:
ae88d32c25e7885d1f6ea11ff362d5f3dcfa276d479221976a6a65ac22fb75f9 - Sigstore transparency entry: 977777840
- Sigstore integration time:
-
Permalink:
amirkarimi/docklift@a25649651c38730c433ee61f858317944008024b -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/amirkarimi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a25649651c38730c433ee61f858317944008024b -
Trigger Event:
release
-
Statement type: