Install Guide • Quick Start • One Command • Profiles • Server Setup • Usage Guide • Docs
FleetDeck is a single-binary deployment platform that takes your application from code to production with one command. It detects your stack, provisions your server, deploys with zero downtime, sets up HTTPS, configures CI/CD, monitors health, and handles backups. No Kubernetes. No YAML hell. Just ship.
fleetdeck deploy ./my-saas --server [email protected] --domain myapp.com --profile saasThat one command connects to your server, detects your app (Next.js, Go, Python, whatever), generates a Docker Compose stack with PostgreSQL + Redis + S3 + email, configures Traefik for automatic HTTPS, deploys your containers, and sets up GitHub Actions CI/CD. Done.
Every new project is the same 30-minute ritual:
- SSH into server, create a Linux user
- Generate SSH keys for CI/CD
- Write a Dockerfile
- Write docker-compose.yml with Traefik labels
- Configure .env with database passwords
- Create GitHub repo, set deploy secrets
- Write a GitHub Actions workflow
- Set up DNS records
- Configure backups
- Pray nothing breaks
Now multiply that by every project you ship. FleetDeck eliminates all of it.
Unlike Kubernetes (too complex), Coolify/CapRover (opinionated PaaS), or plain Docker scripts (too manual), FleetDeck sits in the sweet spot:
- Not a PaaS -- you own your server, your configs, your data
- Not an orchestrator -- no clusters, no service mesh, no learning curve
- Just automation -- it does exactly what you'd do manually, but in seconds
- Single binary -- one
go build, one file, runs everywhere
git clone https://github.com/Artaeon/fleetdeck.git
cd fleetdeck
make build
sudo make installRequirements: Linux (Ubuntu 22.04+, Debian 12+). Go 1.23+ for building. Docker and Traefik are installed automatically by server setup.
Step 1: Provision your server (once):
fleetdeck server setup root@your-server-ip \
--domain yourdomain.com \
--email [email protected] \
--insecure # TOFU: auto-saves host key on first connectThis installs Docker, Traefik (with automatic Let's Encrypt HTTPS), UFW firewall, fail2ban, swap, and hardens SSH. Idempotent -- safe to re-run.
Step 2: Deploy your app:
fleetdeck deploy ./my-app \
--server root@your-server-ip \
--domain myapp.yourdomain.com \
--profile saasStep 3: There is no step 3. Your app is live at https://myapp.yourdomain.com.
# Initialize FleetDeck on your existing server
sudo fleetdeck init
# Import your existing Docker Compose projects
sudo fleetdeck discover
sudo fleetdeck discover import --all
# Create a new project
sudo fleetdeck create myapp --domain myapp.com --template node --profile server
sudo fleetdeck start myappFleetDeck analyzes your project and figures out what you need:
$ fleetdeck detect ./my-app
Application detected!
Property Value
Type nextjs
Language typescript
Framework Next.js (App Router)
Port 3000
Database yes
Redis yes
Confidence 95%
Recommended profile: saasSupported stacks:
| Language | Frameworks | Detection Method |
|---|---|---|
| Node.js | Express, Fastify, Koa | package.json |
| TypeScript | Next.js, NestJS | package.json + app/ dir |
| Python | FastAPI, Django, Flask | requirements.txt, Pipfile, pyproject.toml |
| Go | Gin, Echo, Fiber | go.mod |
| Rust | Actix Web, Axum, Rocket | Cargo.toml |
| Static | HTML/CSS/JS | index.html |
Detection also identifies database usage (PostgreSQL, MySQL, MariaDB), Redis, Docker configs, and recommends the right deployment profile.
Profiles are pre-built infrastructure stacks. Instead of writing Docker Compose from scratch, pick a profile and FleetDeck generates everything.
fleetdeck profiles # List all profiles
fleetdeck profile saas # Inspect a specific profile| Profile | What You Get | Best For |
|---|---|---|
bare |
App container + Traefik HTTPS | Stateless APIs, microservices |
server |
App + PostgreSQL + Redis | Backend APIs, web apps |
saas |
App + PostgreSQL + Redis + S3 (MinIO) + Email (Mailpit) | Full SaaS products |
static |
Nginx + CDN headers + gzip + SPA fallback | Landing pages, docs, SPAs |
worker |
Worker process + Redis queue + PostgreSQL | Background job processors |
fullstack |
Frontend + Backend + PostgreSQL + Redis + S3 | Monorepo applications |
Everything you need for a production SaaS, generated in one command:
- Your app with Traefik routing and automatic HTTPS
- PostgreSQL with health checks, persistent storage,
pg_isreadyprobes - Redis with AOF persistence, 512MB memory limit, LRU eviction
- MinIO (S3-compatible) at
s3.yourdomain.comfor file uploads - Mailpit at
mail.yourdomain.comfor email testing/SMTP relay - Auto-generated
.envwith cryptographically random passwords
For monorepos with separate frontend and backend:
- Frontend at
yourdomain.com(Next.js, React, etc.) - Backend API at
api.yourdomain.com - Separate Dockerfiles (
Dockerfile.frontend,Dockerfile.backend) - Shared database, cache, and object storage
After every zero-downtime deploy, fleetdeck can poll the live domain for a configurable window and automatically roll back if the new revision silently breaks a few minutes after the cutover — the most common production failure mode that health checks alone miss (OOM, upstream latency, slow migration deadlock).
# Watch for 5 minutes, roll back to the pre-deploy snapshot on failure
fleetdeck deploy ./my-app \
--server prod --domain app.example.com \
--watch 5m --watch-rollback
# Tune probing
fleetdeck deploy ./my-app \
--server prod --domain app.example.com \
--watch 10m --watch-interval 15s --watch-threshold 5 --watch-status 200With --watch unset (the default), deploy behavior is unchanged. Without --watch-rollback a failed watchdog only warns — you run fleetdeck rollback manually.
Rollback scope (--watch-rollback-mode):
| Mode | Restores | Loses | Use for |
|---|---|---|---|
full (default) |
config + volumes + DB dumps | none (rewinds fully) | stateless apps, landing pages |
files |
config only | none — DB writes from the watch window survive | stateful apps, mealtime-style workloads |
# Stateful app: roll the config back, but keep user data written during the watch window
fleetdeck deploy ./mealtime --domain mealtime.com \
--watch 5m --watch-rollback --watch-rollback-mode=filesFleetdeck wraps whatever migration tool your project already uses (npm run migrate, rails db:migrate, prisma migrate deploy, flyway migrate, …). It doesn't invent a migration format — it adds three guarantees on top of docker compose exec:
- Snapshot before every run. A pre-migration DB snapshot is taken automatically, so a botched migration is one command away from recovery.
- Tracked history. Every migration is recorded in fleetdeck's SQLite DB with command, status, duration, and the snapshot ID.
- Single-command rollback.
fleetdeck migrate rollback <project>restores the snapshot from the most recent migration.
# Ad-hoc: run a migration, safely
fleetdeck migrate run mealtime --command "npm run migrate"
# Inspect the history — what ran, when, how long, which snapshot
fleetdeck migrate history mealtime
# Restore the pre-migration state of the last migration
fleetdeck migrate rollback mealtime
# Or wire migrations into deploy
fleetdeck deploy ./mealtime --domain mealtime.com \
--migrate "npm run migrate" \
--watch 5m --watch-rollback --watch-rollback-mode=filesWhen --migrate is used with deploy, the pipeline is: snapshot → build → up → migrate → watchdog. A failing migration aborts the deploy before the watchdog runs; the pre-migration snapshot is waiting, and the operator sees the migration output inline.
--migrate is currently supported for local deploys only (where fleetdeck runs on the target server). For remote deploys, SSH to the server and run fleetdeck migrate run there.
fleetdeck deploy ./app --strategy bluegreen --domain app.com| Strategy | How It Works | Downtime |
|---|---|---|
basic |
docker compose up -d |
Brief (~seconds) |
bluegreen |
New containers alongside old, health check, switch traffic | Zero |
rolling |
Update services one at a time with --no-deps |
Zero |
Blue/green deployment flow:
- Start new containers under a temporary project name
- Run health checks against the new containers
- If healthy: stop old containers, promote new ones
- If unhealthy: remove new containers, old ones keep running
Per-project locking prevents concurrent deployments of the same project.
fleetdeck server setup [email protected] \
--domain example.com \
--email [email protected] \
--swap 4| Component | What Gets Configured |
|---|---|
| System | apt update/upgrade, curl, git, htop, fail2ban, UTC timezone |
| Docker | Docker Engine + Compose v2 from official Docker repo |
| Traefik | v3 reverse proxy, Let's Encrypt HTTPS, HTTP-to-HTTPS redirect |
| Firewall | UFW: SSH (22), HTTP (80), HTTPS (443) only |
| Swap | Configurable swap file (default 2GB) |
| SSH | Password auth disabled, root login disabled |
Every step is idempotent -- run it again after six months and it just verifies everything is still configured correctly.
SSH Security: Uses Trust On First Use (TOFU) -- the host key is verified against ~/.ssh/known_hosts. On first connection with --insecure, the key is automatically saved for future verification. SSH keys are auto-discovered by checking ~/.ssh/config, then common key names (id_ed25519, id_ecdsa, id_rsa), then scanning ~/.ssh/ for any private keys -- the --key flag is only needed to override the default.
# Auto-configure root + wildcard A records
fleetdeck dns setup example.com 143.198.1.1 --provider cloudflare --token cf_xxx
# List all records
fleetdeck dns list example.com --token cf_xxxCreates example.com and *.example.com A records pointing to your server. Supports multi-level TLDs (.co.uk, .com.au, etc.) correctly.
fleetdeck env create myapp staging --domain staging.myapp.com
fleetdeck env create myapp preview --domain preview.myapp.com --branch feature/redesign
fleetdeck env promote myapp staging productionEach environment gets its own Docker Compose stack, domain, and configuration. Promotion copies config and images from one environment to another.
Run the monitor as a systemd service so alerts survive reboots and process crashes:
sudo cp packaging/systemd/fleetdeck-monitor.service /etc/systemd/system/
sudo systemctl enable --now fleetdeck-monitor
journalctl -u fleetdeck-monitor -fThe shipped unit runs fleetdeck monitor start --all, which watches every registered project. Put provider webhooks in /etc/fleetdeck/fleetdeck.env (mode 0600) and systemctl restart fleetdeck-monitor to pick them up. See packaging/systemd/README.md for the full setup.
# Continuous monitoring with Slack alerts
fleetdeck monitor start myapp --interval 30s --slack https://hooks.slack.com/xxx
# One-off health check (great for CI/CD)
fleetdeck monitor check myapp- Alerts fire on state transitions only (healthy -> unhealthy, recovery) -- no alert fatigue
- Configurable failure threshold (default: 3 consecutive failures before alerting)
- State persists to disk -- survives process restarts
- Providers: Webhook (JSON POST), Slack (formatted messages), Email (SMTP)
# Full backup (config + database dumps + volume archives)
fleetdeck backup create myapp
# List backups
fleetdeck backup list myapp
# Restore (auto-snapshots current state first)
fleetdeck backup restore myapp <backup-id>
# Quick rollback to latest snapshot
fleetdeck rollback myapp --latest
# Schedule daily backups
fleetdeck schedule enable myapp
# Push a backup to off-server storage (S3, B2, R2, GCS, SFTP, ...)
fleetdeck backup push myappAutomatic snapshots before every stop, restart, destroy, and restore. You can always go back, even after a restore.
What gets backed up: docker-compose.yml, .env, Dockerfile, PostgreSQL dumps (pg_dump), MySQL dumps (mysqldump), Docker volume archives, SHA256 manifest.
Retention: configurable max count, max age (days), max total size (GB). The most recent backup of each type is never deleted.
Off-server backup (rclone driver): add a [backup.remote] block to your config.toml and fleetdeck backup push will mirror backups to any of rclone's ~50 backends without requiring an object-store SDK:
[backup.remote]
driver = "rclone" # currently the only driver
target = "b2:my-fleet-backups" # any rclone remote:path
auto_push = true # optional — push automatically on backup createConfigure the rclone remote once with rclone config on the server. With auto_push = true, every backup is mirrored off-server synchronously; without it, operators run fleetdeck backup push (manually or via cron) to sync on their own schedule.
fleetdeck dashboard --addr :8420Browser-based project management: real-time server stats, project grid, start/stop/restart controls, live log viewer, backup browser, deployment history, and GitHub webhook integration.
Full REST API at /api/projects, /api/status, /api/audit, /api/webhook/github.
Prerequisites: A VPS (Hetzner, DigitalOcean, Linode -- $5/mo works), a domain, and your app code.
# 1. Build FleetDeck
git clone https://github.com/Artaeon/fleetdeck.git && cd fleetdeck
make build && sudo make install
# 2. Provision your server
fleetdeck server setup root@YOUR_SERVER_IP \
--domain yourdomain.com \
--email [email protected] \
--insecure
# 3. Set up DNS (auto if you have Cloudflare)
fleetdeck dns setup yourdomain.com YOUR_SERVER_IP \
--provider cloudflare --token YOUR_CF_TOKEN
# 4. Deploy your app
cd /path/to/your/app
fleetdeck deploy . \
--server root@YOUR_SERVER_IP \
--domain app.yourdomain.com \
--profile saas
# Your app is live at https://app.yourdomain.com# Push code -> GitHub Actions auto-deploys
git push origin main
# Check project status
fleetdeck list
fleetdeck info myapp
fleetdeck logs myapp -f
# Deploy a new project
fleetdeck deploy ./new-project --server root@server --domain new.yourdomain.com
# Create a staging environment
fleetdeck env create myapp staging
# Quick update without CI/CD
fleetdeck update myapp --server prod
fleetdeck update myapp --server prod --service app # single service
# Monitor health
fleetdeck monitor start myapp --slack https://hooks.slack.com/xxx
# Backup before risky changes
fleetdeck backup create myapp
# ... make changes ...
fleetdeck rollback myapp --latest # oops, revertFor apps that are already deployed, fleetdeck update provides a lightweight alternative to full redeployment -- no CI/CD pipeline needed. It syncs your local files to the server, auto-detects Dockerfile or Compose changes, and rebuilds only when necessary.
# Sync files and rebuild only if Dockerfile/compose changed
fleetdeck update myapp --server root@server
# Force a full rebuild (e.g. after dependency changes)
fleetdeck update myapp --server root@server --rebuild --no-cache
# Update a single service in a multi-container stack
fleetdeck update myapp --server root@server --service app
# Pull latest images without rebuilding
fleetdeck update myapp --server root@server --pull
# Just restart containers (no file sync)
fleetdeck update myapp --server root@server --restart-only
# Run hooks before/after deployment
fleetdeck update myapp --server root@server \
--pre-deploy "npm run migrate" \
--post-deploy "npm run seed"FleetDeck is built for running 5-50 projects on a single server:
$ fleetdeck list
NAME DOMAIN STATUS TEMPLATE PROFILE
myapp myapp.com running nextjs saas
api api.company.com running go server
blog blog.company.com running static static
worker - running node worker
staging staging.myapp.com stopped nextjs saasEach project gets its own Linux user, SSH keys, Docker network, and backup schedule. Complete isolation.
Concrete recipes for the scenarios you'll actually hit in production. Each entry assumes fleetdeck is installed on the server and you're running these commands there (either directly via SSH or inside a CI job that SSHes in).
Symptom: fleetdeck migrate run or deploy --migrate reported failure; the DB is in an intermediate state.
# 1. See what ran
fleetdeck migrate history mealtime
# 2. Roll back the DB to the pre-migration snapshot
fleetdeck migrate rollback mealtime
# 3. Confirm the app is serving the old schema + old code
curl -I https://mealtime.comThe pre-migration snapshot was taken before the migration command even started, so restore brings back both the DB and the config files. You're left running the previous release; re-deploy to roll forward once the migration is fixed.
Symptom: the deploy succeeded, containers came up healthy, but 10 minutes in the app started throwing 5xx.
# If you deployed with --watch --watch-rollback, nothing to do —
# the watchdog already restored the pre-deploy snapshot and the
# domain is back on the previous release.
# If you didn't opt into auto-rollback:
fleetdeck rollback mealtime --latestFor next time: always deploy mealtime with --watch 5m --watch-rollback --watch-rollback-mode=files so user writes from the watch window survive.
Symptom: DigitalOcean/Hetzner/whoever lost the disk. You have [backup.remote] configured and backups have been mirrored to B2 / R2 / S3.
# On a fresh server:
fleetdeck server setup root@new-vps --domain example.com --email [email protected]
# Pull the offsite copy of the most recent backup (using rclone directly —
# fleetdeck intentionally leaves this step manual so you confirm the
# source before writing to disk)
rclone copy b2:my-fleet-backups/<backup-id> /opt/fleetdeck/backups/mealtime/<backup-id>
# Rehydrate the project record (the SQLite DB is gone, so fleetdeck
# doesn't know about this backup yet)
fleetdeck create mealtime --domain mealtime.com --profile server --import-backup /opt/fleetdeck/backups/mealtime/<backup-id>
# Or, if `create --import-backup` isn't wired yet, restore manually:
fleetdeck backup restore mealtime <backup-id>Before you need this: test it once end-to-end on a throwaway VPS. A backup you haven't restored is a hope, not a backup.
# Update the env file that systemd reads
sudo $EDITOR /etc/fleetdeck/fleetdeck.env
# edit FLEETDECK_DNS_TOKEN=...
# Restart the daemons so they pick up the new value
sudo systemctl restart fleetdeck-monitor
# Active CLI sessions keep their old env; restart your shell or
# re-export the var before running 'fleetdeck dns ...'Environment-file changes do NOT propagate to already-running processes.
# Update the project's docker compose labels (Traefik Host rule)
# Then re-deploy — Traefik picks up the new host and requests the cert
fleetdeck deploy ./mealtime --domain new.mealtime.com --watch 5mIf DNS isn't pointed yet, Let's Encrypt will reject the certificate request. Point DNS first, then deploy.
A broken backup schedule is a silent failure. The safest pattern is a cron that runs an audit and pings an uptime monitor only if the audit passes — if the ping doesn't arrive, the monitor alerts.
# /etc/cron.d/fleetdeck-backup-audit
*/30 * * * * fleetdeck fleetdeck backup audit --max-age 48h --quiet \
&& curl -fsS https://hc-ping.com/<your-uuid> >/dev/nullIf any project has no backup or its most recent backup is older than
--max-age, audit exits non-zero, the && curl never runs, and
healthchecks.io (or equivalent) fires an alert. Add a second monitor
for the ping endpoint itself.
# Run the verify command — checks all SHA256 checksums and gzip integrity
fleetdeck backup verify mealtime <backup-id>
# For real confidence, do a restore on a throwaway project:
fleetdeck create mealtime-restore-test --domain scratch.example.com --profile server
fleetdeck backup restore mealtime-restore-test <backup-id>Do this at least once per quarter. Verified backups are the only kind that count.
| Env var | Purpose |
|---|---|
FLEETDECK_ENCRYPTION_KEY |
AES key passphrase for encrypted secrets column |
FLEETDECK_API_TOKEN |
Bearer token for the dashboard / HTTP API |
FLEETDECK_WEBHOOK_SECRET |
HMAC secret for GitHub push webhooks |
FLEETDECK_DNS_TOKEN |
Cloudflare API token |
FLEETDECK_MONITORING_SLACK / FLEETDECK_MONITORING_WEBHOOK |
Alert destinations |
FLEETDECK_TRUST_PROXY_IPS |
Comma-separated list of proxy IPs whose X-Forwarded-For is honored for rate limiting |
FLEETDECK_SSH_PASSPHRASE |
Passphrase for an encrypted SSH key used by deploy --server |
FLEETDECK_BASE_PATH |
Override /opt/fleetdeck (e.g. for local/dev use) |
All are also settable in /etc/fleetdeck/config.toml. Env vars win when both are set.
Don't skip items. Each one has saved someone's weekend at least once.
-
fleetdeck server setupon a fresh VPS (idempotent if re-run) -
[backup.remote]configured withauto_push = trueand a tested rclone remote -
fleetdeck backup audit --max-age 48hwired into cron + an uptime monitor (dead man's switch for broken schedules) - Systemd unit
fleetdeck-monitor.serviceenabled (journalctl -u fleetdeck-monitor -fshows probes) - External uptime monitor (Uptime Kuma, Pingdom) configured — fleetdeck's own monitor can't catch "the VPS is dead"
-
FLEETDECK_ENCRYPTION_KEYat least 16 chars of random material (openssl rand -hex 32) - First deploy uses
--watch 5m --watch-rollback(use--watch-rollback-mode=filesfor stateful apps) - Backup restore rehearsed on a throwaway project — verify, don't hope
-
v0.1.0(or whatever release) pinned — don'tupgradethe binary the same week you deploy
| Concern | What fleetdeck does by default | How to tune |
|---|---|---|
| Deploy concurrency | Caps parallel deploys at 3 so a coordinated org push doesn't OOM the box | [server] max_concurrent_deploys = 10 in config.toml |
| Webhook retry storms | Dedupes GitHub redeliveries by X-GitHub-Delivery for 30 min |
No knob — size chosen to outlast GitHub's retry window |
| Stuck subprocess | Every git pull, docker compose build/up/exec runs with a per-step timeout |
Timeouts live at the top of internal/server/webhook.go |
| Graceful shutdown | Shutdown() cancels shutdownCtx, drains in-flight async deploys, then closes DB |
Caller's context deadline caps the wait |
| Rate-limit bypass | X-Forwarded-For only honored when peer is in FLEETDECK_TRUST_PROXY_IPS |
Set to 127.0.0.1,::1 behind a local reverse proxy |
| Secret permissions | .env, .pem, .key, .p12, .pfx, .jks forced to 0600 on upload regardless of source mode |
Source list in internal/remote/transfer.go |
| Audit log exposure | Log file created 0640, directory 0750 — not world-readable |
N/A; tighten with filesystem ACLs if needed |
| Weak encryption key | FLEETDECK_ENCRYPTION_KEY under 16 chars rejected at startup |
Error message points at openssl rand -hex 32 |
| Config typos | Invalid strategy/profile/dns.provider values rejected at Load |
Sentinel values documented in the config reference |
| Command | Description |
|---|---|
| Deploy & Update | |
fleetdeck deploy [dir] |
One-command deploy (local or remote via SSH) |
fleetdeck update <name> |
Lightweight update for live apps (sync, rebuild if needed) |
fleetdeck detect [dir] |
Auto-detect app type and recommend profile |
fleetdeck profiles |
List all deployment profiles |
fleetdeck profile <name> |
Inspect a profile (add --compose for template) |
| Server | |
fleetdeck server setup <user@host> |
Provision a fresh server |
fleetdeck init |
Initialize FleetDeck locally |
fleetdeck upgrade |
Self-update to latest release |
| Projects | |
fleetdeck create <name> |
Create project (--profile, --template, --domain) |
fleetdeck start / stop / restart <name> |
Lifecycle management |
fleetdeck destroy <name> |
Remove project (with optional data retention) |
fleetdeck list / info / logs / status |
Information and monitoring |
| DNS | |
fleetdeck dns setup <domain> <ip> |
Auto-configure A + wildcard records |
fleetdeck dns list / delete |
Record management |
| Environments | |
fleetdeck env create / list / promote / delete |
Multi-environment management |
| Monitoring | |
fleetdeck monitor start <name> |
Continuous health monitoring |
fleetdeck monitor check <name> |
Single health check (exits non-zero if unhealthy) |
| Backup | |
fleetdeck backup create / list / restore |
Full backup and restore |
fleetdeck backup push <name> |
Mirror backup to the rclone remote configured under [backup.remote] |
fleetdeck backup audit --max-age 48h |
Dead-man's-switch: exit non-zero if any project has stale or missing backups |
fleetdeck rollback <name> |
Quick rollback to any snapshot |
fleetdeck snapshot <name> |
Quick snapshot |
fleetdeck schedule enable / disable / list |
Scheduled backups via systemd |
| Migrations | |
fleetdeck migrate run <name> --command "..." |
Snapshot-then-run: tracked app-level migration |
fleetdeck migrate history <name> |
List past migrations with status, duration, snapshot ID |
fleetdeck migrate rollback <name> |
Restore the snapshot from the most recent migration |
| Retention | |
fleetdeck prune deployments --keep 50 |
Trim old deployment rows from the SQLite database |
| CI/CD | |
fleetdeck setup-cd <name> |
Register project for auto-deploy from GitHub |
| Volumes | |
fleetdeck volumes list |
List Docker volumes for a project |
fleetdeck volumes rm |
Remove Docker volumes |
| Discovery | |
fleetdeck discover |
Scan server for existing Docker Compose projects |
fleetdeck discover import |
Import discovered projects |
fleetdeck sync |
Reconcile database with actual system state |
# /etc/fleetdeck/config.toml
[server]
base_path = "/opt/fleetdeck"
domain = "fleet.yourdomain.com"
encryption_key = "your-strong-passphrase"
api_token = "dashboard-auth-token"
webhook_secret = "github-webhook-secret"
[traefik]
network = "traefik_default"
entrypoint = "websecure"
cert_resolver = "letsencrypt"
[github]
default_org = "your-github-org"
[defaults]
template = "node"
postgres_version = "15-alpine"
[deploy]
strategy = "basic" # basic, bluegreen, rolling
default_profile = "server"
timeout = "5m"
[monitoring]
enabled = false
interval = "30s"
timeout = "10s"
failure_threshold = 3
[dns]
provider = "cloudflare"
[backup]
base_path = "/opt/fleetdeck/backups"
max_manual_backups = 10
max_snapshots = 20
max_age_days = 30
max_total_size_gb = 5
auto_snapshot = true
[audit]
enabled = true
log_path = "/var/log/fleetdeck/audit.log"Sensitive values can be set via environment variables: FLEETDECK_API_TOKEN, FLEETDECK_WEBHOOK_SECRET, FLEETDECK_ENCRYPTION_KEY, FLEETDECK_DNS_TOKEN, FLEETDECK_MONITORING_SLACK.
| Layer | Implementation |
|---|---|
| Process isolation | Per-project Linux users with minimal privileges |
| SSH | Ed25519 keys, TOFU host verification, restricted to docker compose only |
| Secrets | AES-256-GCM at rest, PBKDF2 key derivation (100K iterations) |
| Webhooks | HMAC-SHA256 signature verification |
| API | Bearer token auth, per-IP rate limiting (10 req/s) |
| Network | Per-project Docker networks, Traefik TLS termination |
| HTTP | CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy |
| Firewall | UFW: SSH + HTTP + HTTPS only |
| Input validation | Shell injection prevention, path traversal protection |
| Deployment | Per-project file locking prevents concurrent deploys |
| Audit | Structured JSON logs for every operation |
make build # Build binary
make test # Run all tests
make test-race # Run with race detection
make test-cover # Generate coverage report
make vet # Run go vet
make lint # Run golangci-lint
make release # Build release binaries (amd64 + arm64)900+ test functions across 60+ test files. All tests pass. Zero race conditions.
| Package | Coverage | Tests |
|---|---|---|
| detect | 99.1% | 78 |
| profiles | 95.1% | 54 |
| bootstrap | 93.9% | 56 |
| config | 90.7% | 26 |
| environments | 88.7% | 51 |
| dns | 85.7% | 40 |
| monitor | 82.1% | 56 |
| project | 73.2% | 39 |
| deploy | 32.6% | 39 |
| remote | 9.0% | 36 |
Deploy and remote have lower unit test coverage because they execute docker compose and SSH commands that require real infrastructure. Integration tests in CI run these against real Docker and SSH containers.
Every push runs: build, vet, race-detected tests, coverage report, Docker integration tests, and SSH integration tests. See .github/workflows/ci.yml.
- Smart app detection (Node, Python, Go, Rust, static)
- 6 deployment profiles (bare, server, saas, static, worker, fullstack)
- One-command remote deployment via SSH
- Server provisioning (Docker, Traefik, firewall, SSL)
- Zero-downtime deployments (blue/green, rolling)
- TOFU SSH host key verification
- Per-project deployment locking
- Health monitoring with Slack/webhook/email alerts + state persistence
- DNS management with multi-level TLD support (Cloudflare)
- Environment management (staging/production/preview)
- Web dashboard with REST API
- Backup, snapshot, and rollback system
- Secret encryption (AES-256-GCM)
- Audit logging with rotation
- GitHub Actions CI with integration tests
- Lightweight update command for live applications
- SSH key auto-discovery from ~/.ssh/config
- CI/CD setup with branch-based deployments
- Hetzner and DigitalOcean DNS providers
- Resource monitoring (CPU, RAM per project via cgroups)
- Prometheus metrics endpoint
- Plugin system for custom hooks
- Multi-server support
See CONTRIBUTING.md for development setup, code style, and pull request guidelines.
See SECURITY.md for the security model and vulnerability reporting.
MIT
Built by Artaeon