FTL is a deployment tool that reduces complexity for projects that don't require extensive orchestration infrastructure. It provides automated deployment to cloud providers like Hetzner, DigitalOcean, Linode, and custom servers without the overhead of CI/CD pipelines or container orchestration platforms.
- Single YAML configuration file with environment variable substitution
- Zero-downtime deployments
- Automatic SSL/TLS certificate management
- Docker-based deployment with built-in health checks
- Integrated Nginx reverse proxy
- Multi-provider support (Hetzner, DigitalOcean, Linode, custom servers)
- Fetch and stream logs from deployed services
-
Via Homebrew (macOS and Linux)
brew tap yarlson/ftl brew install ftl
-
Download from GitHub releases
curl -L https://github.com/yarlson/ftl/releases/latest/download/ftl_$(uname -s)_$(uname -m).tar.gz | tar xz sudo mv ftl /usr/local/bin/
-
Build from source
go install github.com/yarlson/ftl@latest
Create an ftl.yaml
configuration file in your project directory:
project:
name: my-project
domain: my-project.example.com
email: my-project@example.com
servers:
- host: my-project.example.com
port: 22
user: my-project
ssh_key: ~/.ssh/id_rsa
services:
- name: my-app
image: my-app:latest
port: 80
health_check:
path: /
interval: 10s
timeout: 5s
retries: 3
routes:
- path: /
strip_prefix: false
dependencies:
- name: postgres
image: postgres:16
volumes:
- postgres_data:/var/lib/postgresql/data
env:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_USER=${POSTGRES_USER:-postgres}
- POSTGRES_DB=${POSTGRES_DB:-app}
volumes:
- postgres_data
Environment variables in the configuration can be:
- Required:
${VAR_NAME}
- Must be set in the environment - Optional with default:
${VAR_NAME:-default_value}
- Uses default if not set
Set up your server with the required dependencies:
ftl setup
This command will:
- Install Docker and other necessary packages on your server
- Configure firewall rules
- Set up user permissions
- Initialize Docker networks
Deploy your application to the configured servers:
ftl deploy
This command will:
- Connect to your servers via SSH
- Pull Docker images specified in your configuration
- Start new containers with health checks
- Configure the Nginx reverse proxy
- Manage SSL/TLS certificates via ACME
- Perform zero-downtime container replacement
- Clean up unused resources
Retrieve logs from your deployed services:
ftl logs [service] [flags]
- service: (Optional) Name of the service to fetch logs from. If omitted, logs from all services are fetched.
- flags:
-f
,--follow
: Stream logs in real-time.-n
,--tail
: Number of lines to show from the end of the logs.
-
Fetch logs from all services:
ftl logs
-
Stream logs from a specific service:
ftl logs my-app -f
-
Fetch the last 50 lines of logs from all services:
ftl logs -n 50
FTL manages deployments and log retrieval through these main components:
- Installs required packages (Docker, basic tools)
- Configures firewall rules
- Sets up user permissions
- Initializes Docker networks
- Connects to configured servers via SSH
- Pulls specified Docker images
- Starts new containers with health checks
- Configures Nginx reverse proxy
- Manages SSL/TLS certificates via ACME
- Performs zero-downtime container replacement
- Cleans up unused resources
- Fetches logs from specified services
- Supports real-time streaming with the
-f
flag - Allows limiting the number of log lines with the
-n
flag
- Web applications with straightforward deployment needs
- Projects requiring automated SSL and reverse proxy setup
- Small to medium services running on single or multiple servers
- Teams seeking to minimize deployment infrastructure
- Applications requiring environment-specific configurations
- Complex microservice architectures requiring service mesh
- Systems needing advanced orchestration features
- Multi-region deployment coordination
- Specialized compliance environments
project:
name: string # Project identifier (required)
domain: string # Primary domain (required, must be FQDN)
email: string # Contact email (required, valid email format)
servers:
- host: string # Server hostname/IP (required, FQDN or IP)
port: int # SSH port (required, 1-65535)
user: string # SSH user (required)
ssh_key: string # Path to SSH key file (required)
services:
- name: string # Service identifier (required)
image: string # Docker image (required)
port: int # Container port (required, 1-65535)
path: string # Service path (default: "./")
command: string # Override container command
entrypoint: [string] # Override container entrypoint
health_check:
path: string # Health check endpoint
interval: duration # Time between checks
timeout: duration # Check timeout
retries: int # Number of retries
routes:
- path: string # Route path prefix (required)
strip_prefix: bool # Strip prefix from requests
volumes: [string] # Volume mappings (format: "volume:path")
env: # Environment variables
- KEY=value
dependencies:
- name: string # Dependency name (required)
image: string # Docker image (required)
volumes: [string] # Volume mappings (format: "volume:path")
env: # Environment variables
- KEY=value
volumes: [string] # Named volumes list
FTL supports two forms of environment variable substitution in the configuration:
-
Required Variables:
${VAR_NAME}
- Must be present in the environment
- Deployment fails if variable is not set
-
Variables with Defaults:
${VAR_NAME:-default_value}
- Uses the environment variable if set
- Falls back to the default value if not set
- Health Checks: Customize health check endpoints, intervals, timeouts, and retries for each service.
- Volume Management: Define named volumes for persistent data storage.
- Environment Variables: Set environment variables for services and dependencies, with support for environment variable substitution.
- Service Dependencies: Specify dependent services and their configurations.
- Routing Rules: Define custom routing paths and whether to strip prefixes.
# Clone repository
git clone https://github.com/yarlson/ftl.git
# Install dependencies
cd ftl
go mod download
# Run tests
go test ./...
Contributions are welcome. Please ensure:
- Code follows project style guidelines
- Tests pass and new tests are added for new features
- Documentation is updated accordingly