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-hetzner helper.
  • 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

  1. Install Docker, Docker Compose, and Git
  2. Create the system user and directory structure (see above)
  3. Clone the repository to /opt/devpush
  4. Create .env (you can start from /opt/devpush/.env.example) with all required variables
  5. Create a GitHub App and add credentials to .env
  6. Build runner images: sudo /opt/devpush/scripts/build-runners.sh
  7. Ensure code and data are owned by the system user:
sudo chown -R devpush:devpush /opt/devpush /var/lib/devpush
  1. 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.