Push Button Deploy
One command takes you from an empty directory to a freshly generated Phoenix (or Sinatra) app serving HTTPS on a production DigitalOcean droplet, with a CI/CD pipeline that deploys every push to main from that moment on.
The Problem
When I develop a new idea, I prioritize rapid prototyping. Previously, after creating a prototype, deployment required numerous manual steps or custom Terraform configurations, which proved inefficient. This utility addresses those challenges by automating the deployment pipeline. The pipeline is established at the outset, ensuring that every commit I make is deployed immediately.
What I built
push-button-deploy — a one-command path from an empty directory to a production app serving HTTPS, with CI/CD attached from the first push.
./bootstrap.sh ~/src/myapp
… a few minutes later …
LIVE: https://myapp.example.com
The command scaffolds the application, whether it is Phoenix, Sinatra, or a Zola static site, provisions a DigitalOcean droplet and DNS using Terraform, configures remote state, and initializes a GitHub repository with deployment and rollback workflows. Deployment is considered successful only after the site responds over HTTPS. Subsequently, every push to the main branch triggers an automatic deployment.
The runtime environment consists of a compact self-hosted platform. A shared Caddy instance on the droplet terminates TLS and routes traffic to multiple applications, each operating as an independent Docker Compose project on an internal network. This configuration enables several sites to share a single $6 droplet without interference. Deployments are zero-downtime: the new version is launched alongside the existing one, must pass a health check, and only then assumes control, while Caddy queues requests during the transition. Database migrations are executed prior to the swap; if they fail, the previous version continues to serve requests.
Applications use SQLite by default, with two backup strategies: Litestream continuously streams the write-ahead log to object storage for point-in-time recovery, and a sidecar process periodically creates full-database archives for long-term restoration. Applications that exceed SQLite’s capabilities can transition to managed PostgreSQL, with application infrastructure and data maintained in separate Terraform states to ensure that removing a droplet does not affect the database. Static sites do not utilize containers; continuous integration builds the site, delivers a tarball, and the new release is activated through a single atomic rename.