Installation
How to install /dev/push on a server.
Prerequisites
- Server: Ubuntu 20.04+ or Debian 11+ with SSH access and sudo privileges. 4GB+ RAM recommended. A Hetzner CPX31 (~$15/mo) works well. If you're using Hetzner Cloud, you can provision a hardened server with the
devpush-hetznerhelper. - DNS: We recommend Cloudflare.
- GitHub account: You'll create a GitHub App for login and repository access.
- Email provider: A Resend account for login emails and invitations.
Cloudflare SSL
Set SSL/TLS mode to "Full (strict)" and keep records "Proxied".
HTTP-01 works through the proxy as long as port 80 is reachable; DNS-01 is still recommended for wildcards or if you want to skip port 80 entirely.
Cloudflare's free Universal SSL only covers first-level wildcards (*.example.com), not sub-subdomains (*.sub.example.com). If your deployment domain is a subdomain, disable Cloudflare proxy for deployment records, use a separate first-level domain, or purchase Advanced Certificate Manager.
Quick installation
1. Run the installer
curl -fsSL https://install.devpu.sh | sudo bash
2. Create the GitHub App
3. Configure
Paste the GitHub App credentials and fill in the remaining values:
sudo nano /var/lib/devpush/.env
We recommend you use Cloudflare (DNS-01)
With DNS-01, you get a wildcard certificate in one go, which reduces issuance churn for new deployments.
Set CERT_CHALLENGE_PROVIDER=cloudflare (leave it as default for HTTP-01) and add CF_DNS_API_TOKEN. See Cloudflare setup.
4. Set DNS
Before starting the service, ensure your DNS records are configured and propagated. You can keep the app and deployments on separate domains/subdomains if you want extra isolation.
| Type | Name | Value | Purpose |
|---|---|---|---|
| A | example.com |
SERVER_IP |
App hostname |
| A (wildcard) | *.example.com |
SERVER_IP |
Deployments |
Verify DNS propagation:
dig example.com +short
# Should show your server IP (or Cloudflare IPs if proxied)
DNS must resolve before starting
Let's Encrypt will attempt to verify your domain when the service starts. Ensure DNS has propagated and your server is accessible on ports 80 and 443. HTTP-01 works with Cloudflare's proxy as long as port 80 is open; for wildcard certificates or to avoid port 80 entirely, use DNS-01 with CERT_CHALLENGE_PROVIDER=cloudflare.
5. Start
sudo systemctl start devpush.service
Visit https://example.com (your APP_HOSTNAME).
If you run into issues, see the troubleshooting section below.
You can now start deploying your apps.
Manual installation
No automated updates
Manual installations don't create /var/lib/devpush/version.json, which tracks the installed version. The update script won't run upgrade hooks without it.
Requirements
| Component | Details |
|---|---|
| Packages | Docker Engine, Docker Compose v2+, Git |
| System user | e.g., devpush (UID/GID needed for .env) |
Directory structure (owned by system user):
| Path | Mode | Description |
|---|---|---|
/opt/devpush |
0755 |
Application code |
/var/lib/devpush |
0750 |
Data directory |
/var/lib/devpush/.env |
0600 |
Configuration |
/var/lib/devpush/traefik |
0750 |
Traefik config/certs |
/var/lib/devpush/upload |
0750 |
Uploaded files |
Steps
- Install Docker, Docker Compose, and Git
- Create the system user and directory structure (see above)
- Clone the repository to
/opt/devpush - Create
.env(you can start from/opt/devpush/.env.example) with all required variables - Create a GitHub App and add credentials to
.env - Build runner images:
sudo /opt/devpush/scripts/build-runners.sh - Ensure code and data are owned by the system user:
sudo chown -R devpush:devpush /opt/devpush /var/lib/devpush
- Install the systemd service:
sudo install -m 0644 /opt/devpush/scripts/devpush.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now devpush.service
If you run into issues, see the troubleshooting section below.
Troubleshooting
If the service fails to start, check the status and logs:
# Service status + recent logs
sudo systemctl status devpush.service
sudo journalctl -u devpush.service -e -f
If you're running into issues, it's likely with the app or Traefik. You can get more detailed logs with:
# Container logs (e.g., Traefik/TLS or app)
sudo /opt/devpush/scripts/compose.sh logs -f traefik
sudo /opt/devpush/scripts/compose.sh logs -f app
Common errors with Traefik:
- DNS doesn't resolve to the server IP yet.
- Provider API token (e.g.,
CF_DNS_API_TOKEN) is missing zone access (Zone:Read+Zone:DNS:Edit) or points to the wrong account/zone. - Let's Encrypt rate limit hit after repeated failures; wait about an hour before retrying.