Rustland
A fast CLI tool for setting up local development environments using Docker, written in Rust.
Table of Contents
- Features
- Requirements
- Installation
- Quick Start
- Recipes
- Configuration
- Production Deployment
- Services
- Commands
- Tooling
- How it Works
- Project Structure
- Contributing
Features
- Fast startup - Built with Rust for quick execution
- Simple configuration - YAML-based
.rustland.ymlconfig file - HTTPS support - Automatic HTTPS via Traefik reverse proxy with self-signed certificates
- No hosts file modification - Uses
lvh.mewildcard DNS (resolves to 127.0.0.1) - Docker native - Direct Docker API communication using bollard
- Recipe system - Pre-configured stacks for Laravel, WordPress, Drupal, and more
- Tooling support - Run composer, npm, artisan directly via
rustland <tool> - Production ready - Environment support, restart policies, resource limits, health checks
- Export to Docker Compose - Deploy your environment anywhere with
rustland export
Requirements
- Docker Engine or Docker Desktop
- Linux x86_64
Installation
Quick Install (Linux x86_64)
curl -sSL https://raw.githubusercontent.com/civanmoreno/rustland/main/install.sh | bash
Manual Installation
-
Download the latest release from GitHub Releases
- Extract the archive:
tar -xzf rustland-*-linux-x86_64.tar.gz - Move to your PATH:
sudo mv rustland /usr/local/bin/ - Verify installation:
rustland help
Build from Source
# Clone the repository
git clone https://github.com/civanmoreno/rustland.git
cd rustland
# Build the project
cargo build --release
# The binary will be at ./target/release/rustland
sudo cp ./target/release/rustland /usr/local/bin/
Quick Start
# Initialize with a recipe
rustland init --recipe laravel
# Or use interactive mode
rustland init
# Start the environment
rustland start
# Access your app at https://your-project.lvh.me:8443
Recipes
Rustland comes with pre-configured recipes for common development stacks:
| Recipe | Description | Services |
|---|---|---|
laravel |
Laravel PHP framework | PHP, Nginx, MySQL, Redis |
wordpress |
WordPress CMS | PHP, Nginx, MariaDB |
drupal |
Drupal CMS | PHP, Nginx, PostgreSQL, Redis |
lamp |
LAMP stack | PHP, Apache, MySQL |
lemp |
LEMP stack | PHP, Nginx, MySQL |
node |
Node.js runtime | Node.js |
fullstack |
Full stack development | PHP, Nginx, Node.js, MySQL, Redis |
custom |
Interactive configuration | Choose your services |
Using Recipes
# Initialize with Laravel recipe
rustland init --recipe laravel
# Initialize with WordPress recipe
rustland init --recipe wordpress
# Initialize with Node.js recipe
rustland init --recipe node
# Interactive mode (shows recipe selection)
rustland init
Configuration
Rustland uses a .rustland.yml file in your project root.
Example: Laravel with MySQL
name: my-laravel-app
services:
appserver:
type: php
version: "8.2"
variant: fpm
webroot: ./public
xdebug: true
webserver:
type: nginx
version: latest
webroot: ./public
database:
type: mysql
version: "8.0"
credentials:
database: laravel
user: laravel
password: secret
cache:
type: redis
version: "7"
proxy:
webserver:
- my-laravel-app.lvh.me
Example: Laravel with PostgreSQL
name: my-app
services:
appserver:
type: php
version: "8.3"
variant: fpm
webroot: ./public
xdebug: true
webserver:
type: nginx
version: latest
webroot: ./public
database:
type: postgresql
version: "16"
credentials:
database: myapp
user: myapp
password: secret
cache:
type: redis
version: "7"
proxy:
webserver:
- my-app.lvh.me
Example: WordPress with MariaDB
name: my-wordpress
services:
appserver:
type: php
version: "8.2"
variant: fpm
webroot: ./
xdebug: true
webserver:
type: nginx
version: latest
webroot: ./
database:
type: mariadb
version: "10.11"
credentials:
database: wordpress
user: wordpress
password: wordpress
proxy:
webserver:
- my-wordpress.lvh.me
Example: Node.js (Next.js/Vite)
name: my-frontend
services:
node:
type: node
version: "20"
command: "npm install && npm run dev"
ports:
- "3000:3000"
environment:
HOST: "0.0.0.0"
Example: Full Stack (PHP + Node + MySQL + Redis)
name: fullstack-app
services:
appserver:
type: php
version: "8.2"
variant: fpm
webroot: ./public
xdebug: true
webserver:
type: nginx
version: latest
webroot: ./public
database:
type: mysql
version: "8.0"
credentials:
database: app
user: app
password: secret
cache:
type: redis
version: "7"
frontend:
type: node
version: "20"
command: "npm run dev"
ports:
- "5173:5173"
environment:
HOST: "0.0.0.0"
proxy:
webserver:
- fullstack-app.lvh.me
Example: Production Configuration with Workers
name: my-production-app
env: prod # Environment: dev, staging, prod
env_file: # Load environment files
- .env
- .env.local
services:
appserver:
type: php
version: "8.2"
variant: fpm
webroot: ./public
restart: always # Restart policy for production
resources: # Resource limits
cpus: "1"
memory: "512m"
healthcheck: # Health check
test: "php-fpm-healthcheck || exit 1"
interval: "30s"
timeout: "10s"
retries: 3
depends_on: # Start order
- database
- cache
worker:
type: php
version: "8.2"
variant: cli # CLI variant for workers
command: "php artisan queue:work --sleep=3 --tries=3"
restart: always
resources:
cpus: "0.5"
memory: "256m"
depends_on:
- database
- cache
webserver:
type: nginx
version: latest
webroot: ./public
restart: always
database:
type: mysql
version: "8.0"
restart: always
resources:
cpus: "2"
memory: "1g"
credentials:
database: app
user: app
password: ${DB_PASSWORD}
cache:
type: redis
version: "7"
restart: always
proxy:
webserver:
- my-production-app.lvh.me
tooling:
composer:
service: appserver
cmd: composer
artisan:
service: appserver
cmd: php artisan
Production Deployment
Export to Docker Compose
Export your Rustland configuration to a docker-compose.yml file for production deployment:
# Export for production
rustland export --env prod
# Export with custom output file
rustland export --output docker-compose.prod.yml --env prod
Environment-based Configuration
Rustland supports different environments that affect default behaviors:
| Environment | Restart Policy | Description |
|---|---|---|
dev |
no |
Development (default) |
staging |
on-failure |
Staging/testing |
prod |
always |
Production |
Environment Files
Load environment variables from .env files:
name: my-app
env_file:
- .env # Base environment
- .env.local # Local overrides (git-ignored)
- .env.${ENV} # Environment-specific
Restart Policies
| Policy | Description |
|---|---|
no |
Never restart (default for dev) |
always |
Always restart (default for prod) |
on-failure |
Restart only on failure |
unless-stopped |
Restart unless explicitly stopped |
Resource Limits
Limit CPU and memory usage per service:
services:
appserver:
type: php
version: "8.2"
resources:
cpus: "1.5" # 1.5 CPU cores
memory: "512m" # 512 MB memory
memory_reservation: "256m" # Soft limit
Health Checks
Configure health checks to monitor service status:
services:
database:
type: mysql
version: "8.0"
healthcheck:
test: "mysqladmin ping -h localhost"
interval: "30s"
timeout: "10s"
retries: 3
start_period: "60s"
Workers and Background Processes
Run background workers using the CLI variant:
services:
# Queue worker
worker:
type: php
version: "8.2"
variant: cli
command: "php artisan queue:work"
restart: always
# Scheduler
scheduler:
type: php
version: "8.2"
variant: cli
command: "php artisan schedule:work"
restart: always
Services
| Service | Type | Versions | Docker Image |
|---|---|---|---|
| PHP | php |
8.3, 8.2, 8.1, 8.0, 7.4 | serversideup/php:{version}-{variant} |
| Nginx | nginx |
latest | nginx (official) |
| MySQL | mysql |
8.4, 8.0, 5.7 | mysql (official) |
| MariaDB | mariadb |
10.x, 11.x | mariadb (official) |
| PostgreSQL | postgresql |
15, 14, 13 | postgres (official) |
| Redis | redis |
7, 6 | redis (official, alpine) |
| Node.js | node |
20, 18, 16 | node (official, alpine) |
PHP Image
We use serversideup/php for PHP with configurable variants.
Available variants:
| Variant | Image | Use case |
|---|---|---|
fpm (default) |
serversideup/php:8.2-fpm |
Web apps with Nginx/Apache |
fpm-alpine |
serversideup/php:8.2-fpm-alpine |
Lightweight web apps |
cli |
serversideup/php:8.2-cli |
CLI scripts, workers, queues |
cli-alpine |
serversideup/php:8.2-cli-alpine |
Lightweight CLI apps |
Configuration example:
services:
appserver:
type: php
version: "8.2"
variant: fpm # optional, defaults to "fpm"
worker:
type: php
version: "8.2"
variant: cli # for queue workers
Why serversideup/php?
- Pre-installed extensions: pdo_mysql, pdo_pgsql, gd, zip, intl, bcmath, opcache
- Xdebug support: Included and ready for debugging
- Optimized for frameworks: Laravel, Symfony, WordPress
- Works out of the box: No custom Dockerfiles needed
Nginx
We use the official nginx image.
Image format: nginx:{version}
Examples:
nginx:latestnginx:1.25nginx:alpine
Configuration example:
services:
webserver:
type: nginx
version: latest # or "1.25", "1.24", "alpine"
webroot: ./public
Features:
- Official Docker image, well maintained
- Automatically configured to work with PHP-FPM
- Custom nginx config generated in
.rustland/nginx.conf
MySQL
We use the official mysql image.
Image format: mysql:{version}
Available versions: 8.4, 8.0, 5.7
Examples:
mysql:8.4mysql:8.0mysql:5.7
Configuration example:
services:
database:
type: mysql
version: "8.0"
credentials:
database: myapp
user: myapp
password: secret
Environment variables set automatically:
MYSQL_DATABASE- Database nameMYSQL_USER- Database userMYSQL_PASSWORD- User passwordMYSQL_ROOT_PASSWORD- Root password (same as user password)
MariaDB
We use the official mariadb image.
Image format: mariadb:{version}
Available versions: 11.x, 10.x (10.11, 10.6, etc.)
Examples:
mariadb:11.2mariadb:10.11mariadb:10.6
Configuration example:
services:
database:
type: mariadb
version: "10.11"
credentials:
database: myapp
user: myapp
password: secret
Environment variables set automatically:
MARIADB_DATABASE- Database nameMARIADB_USER- Database userMARIADB_PASSWORD- User passwordMARIADB_ROOT_PASSWORD- Root password
PostgreSQL
We use the official postgres image.
Image format: postgres:{version}
Available versions: 16, 15, 14, 13
Examples:
postgres:16postgres:15postgres:14
Configuration example:
services:
database:
type: postgresql
version: "15"
credentials:
database: myapp
user: myapp
password: secret
Environment variables set automatically:
POSTGRES_DB- Database namePOSTGRES_USER- Database userPOSTGRES_PASSWORD- User password
Redis
We use the official redis Alpine image for a lightweight footprint.
Image format: redis:{version}-alpine
Available versions: 7, 6
Examples:
redis:7-alpineredis:6-alpine
Configuration example:
services:
cache:
type: redis
version: "7"
Features:
- Alpine-based image (smaller size ~30MB)
- No authentication by default (suitable for local development)
- Persistent data stored in Docker volume
Node.js
We use the official node Alpine image.
Image format: node:{version}-alpine
Available versions: 22, 20, 18, 16
Examples:
node:22-alpinenode:20-alpinenode:18-alpine
Configuration example:
services:
node:
type: node
version: "20"
command: "npm run dev"
ports:
- "3000:3000"
Features:
- Alpine-based image (smaller size)
- Use
commandto run your dev server - Use
portsto expose your application
Example for Vite/Next.js:
services:
frontend:
type: node
version: "20"
command: "npm install && npm run dev"
ports:
- "5173:5173" # Vite default port
environment:
HOST: "0.0.0.0" # Required for container access
Commands
| Command | Description |
|---|---|
rustland init |
Initialize a new project with interactive prompts |
rustland init --recipe laravel |
Initialize with a specific recipe |
rustland start |
Start all services |
rustland stop |
Stop all services |
rustland destroy |
Remove all containers, networks and volumes |
rustland status |
Show status of all services |
rustland logs [service] |
View logs for a service |
rustland shell [service] |
Open a shell in a container |
rustland rebuild |
Rebuild containers |
rustland config |
Show current configuration |
rustland info |
Show project information and configuration summary |
rustland export |
Export to docker-compose.yml for production |
rustland db import |
Import SQL file to database |
rustland db export |
Export database to SQL file |
Tooling
Rustland allows you to run tools inside your containers without manually using docker exec. Tools are configured in the tooling section of your .rustland.yml.
Using Tools
# Run composer
rustland composer install
rustland composer require laravel/sanctum
# Run artisan (Laravel)
rustland artisan migrate
rustland artisan make:controller UserController
# Run npm
rustland npm install
rustland npm run dev
# Run php
rustland php -v
rustland php artisan tinker
Default Tools by Recipe
| Recipe | Available Tools |
|---|---|
| Laravel | composer, artisan, php, npm |
| WordPress | wp, composer, php |
| Drupal | drush, composer, php |
| LAMP/LEMP | composer, php |
| Node | npm, node, yarn, npx |
| Full Stack | composer, artisan, php, npm, node, yarn |
Custom Tooling Configuration
You can define custom tools in your .rustland.yml:
name: my-project
services:
appserver:
type: php
version: "8.2"
# ... other services
tooling:
composer:
service: appserver
cmd: composer
artisan:
service: appserver
cmd: php artisan
php:
service: appserver
cmd: php
npm:
service: appserver
cmd: npm
test:
service: appserver
cmd: php artisan test
lint:
service: appserver
cmd: ./vendor/bin/pint
Tool Options
| Option | Description |
|---|---|
service |
Container to run the command in |
cmd |
Base command to execute |
user |
User to run command as (optional) |
Examples
# Install dependencies
rustland composer install
rustland npm install
# Run database migrations
rustland artisan migrate
# Run tests
rustland artisan test
# Create a new controller
rustland artisan make:controller PostController
# Run code formatter
rustland lint
# Interactive PHP shell
rustland php artisan tinker
How it Works
- Init -
rustland initcreates a.rustland.ymlconfiguration file - Start -
rustland startreads the config and:- Creates a Docker network for the project
- Starts Traefik proxy (if not running) on ports 8080/8443
- Creates and starts containers for each service
- Configures routing through Traefik
- Access - Your app is available at
https://{project-name}.lvh.me:8443
Networking
- Each project gets its own Docker network
- Traefik acts as a reverse proxy for all projects
- Uses
lvh.mewildcard DNS which resolves*.lvh.meto127.0.0.1 - No need to modify
/etc/hosts
HTTPS
- Traefik generates self-signed certificates automatically
- Certificates are stored in
~/.config/rustland/certs/ - Access via port 8443 (HTTPS) or 8080 (HTTP)
Project Structure
rustland/
├── src/
│ ├── main.rs # Entry point
│ ├── cli/
│ │ ├── mod.rs # CLI definition
│ │ └── commands/ # Command implementations
│ ├── config/
│ │ ├── schema.rs # Configuration structs
│ │ └── validation.rs # Config validation
│ ├── docker/
│ │ ├── client.rs # Docker API client
│ │ ├── compose.rs # Compose generation
│ │ ├── network.rs # Network management
│ │ └── proxy.rs # Traefik proxy
│ ├── services/ # Service definitions
│ ├── templates/ # Config templates
│ └── utils/ # Utilities
├── Cargo.toml
└── README.md
Comparison with Lando
Rustland is inspired by Lando but with some differences:
| Feature | Rustland | Lando |
|---|---|---|
| Language | Rust | Node.js |
| Startup time | Fast | Moderate |
| Configuration | .rustland.yml |
.lando.yml |
| HTTPS | Traefik + self-signed | Traefik + CA |
| Docker Compose | Direct Docker API | docker-compose |
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request on GitHub.