PodWarden Hub
pw-launcher

Operator Quickstart

Install pw-launcher on a Docker host and run your first deploy in under ten steps.

This guide takes you from a bare Docker host to a running pw-launcher instance and a successful first deploy. It assumes you have network access to registry.podwarden.com and a PodWarden instance already running on the same host.

Prerequisites

  • Docker Engine 24+ with the Compose plugin. Confirm with docker compose version — if it prints a version number, you are good.
  • Registry read credentials for registry.podwarden.com. Obtain the username and token from your password manager or team credentials store.
  • A running PodWarden instance on the same host. pw-launcher needs its compose file path and database container name to manage deployments.

Steps

1. Clone the launcher repo

git clone https://git.mediablade.net/podwarden/pw-launcher.git ~/pw-launcher
cd ~/pw-launcher/infra

This gives you deploy.sh, docker-compose.yml, and .env.example under infra/.

2. Create and secure the environment file

cp .env.example .env
chmod 600 .env

Open .env in your editor. You need to fill in four values before the launcher will start: the image tag to run (PW_TAG), the registry credentials, the session secret, and the basic-auth credentials. The following steps generate the secrets.

3. Set the image tag

In .env, set PW_TAG to the CalVer release you want to run, for example:

PW_TAG=v2026.04.24.2

Use the latest release tag from the registry. You can list available tags at any time from any system with Docker installed:

docker run --rm curlimages/curl -s \
  https://registry.podwarden.com/v2/podwarden/pw-launcher-api/tags/list

4. Generate the basic-auth password hash

pw-launcher protects all API routes with HTTP Basic auth. Generate a bcrypt hash of your chosen password:

docker run --rm httpd:2.4-alpine htpasswd -nbB -C 12 operator 'your-password-here' \
  | cut -d: -f2

The output is a string beginning with $2y$12$.... In .env, set:

PW_BASIC_AUTH_USER=operator
PW_BASIC_AUTH_PASSWORD_HASH='$2y$12$...'

Wrap the hash value in single quotes in .env. The $ characters in a bcrypt hash will be expanded by the shell if the value is unquoted.

5. Generate the session secret

openssl rand -base64 32

Paste the output after PW_SESSION_SECRET= in .env.

6. Configure your stack

Copy the example stack config and edit it for your environment:

mkdir -p config
cp services.yaml.example config/services.yaml

Open config/services.yaml. The file lists each stack (deployment target) the launcher will manage. At minimum you need:

  • name — a short slug for the stack (for example, podwarden)
  • registry_repo — the image repository without the registry prefix (for example, podwarden/podwarden-api)
  • compose_file — the absolute path to the Compose file on this host
  • compose_service — the service name within that Compose file to restart
  • health_check.url — the HTTP URL pw-launcher will poll after a deploy to confirm the service is healthy
  • backup.type — set to postgres if the stack has a database (required for full rollback), or none

A minimal entry for a PodWarden stack looks like:

stacks:
  - name: podwarden
    display_name: PodWarden Core
    registry_repo: podwarden/podwarden-api
    compose_file: /opt/podwarden/docker-compose.yml
    compose_service: podwarden-api
    health_check:
      url: http://localhost:8000/health
      timeout_s: 30
    backup:
      type: postgres
      container: podwarden-db
      database: podwarden
      user: podwarden

7. Log into the registry and deploy

# Log in once — credentials are cached by Docker
docker login registry.podwarden.com

# Start pw-launcher
./deploy.sh

deploy.sh pulls the pw-launcher-api and pw-launcher-ui images, starts both containers, and waits for the health-check to pass. It is idempotent: running it again with the same PW_TAG is a no-op; running it with a new tag rolls both images.

Expected output ends with something like:

pw-launcher-api is healthy
pw-launcher-ui  is healthy
pw-launcher is running at http://localhost:8080

8. Verify the API is responding

curl -fsS http://localhost:8080/api/health \
  -u "operator:your-password-here"

Expected response: {"status":"ok"}. If you receive a 401, check that PW_BASIC_AUTH_USER and PW_BASIC_AUTH_PASSWORD_HASH in .env match the credentials you are using here.

9. Open the UI and run your first deploy

Open http://<your-server-ip>:8080 in a browser. You will be prompted for your basic-auth credentials. After signing in you land on the Builds tab.

To run your first deploy:

  1. Find the build you want to deploy in the Builds list. Look for the row tagged :production or a specific CalVer tag.
  2. Click the expand arrow on that row.
  3. Click Deploy.
  4. Watch the six-step progress sequence complete in the browser. When the status reads DEPLOYED, the new image is running and the health-check passed.

The Timeline tab now shows this deploy as your first history entry.

Optional: run under systemd

If you want pw-launcher to start automatically on boot and restart after crashes, install the included systemd unit:

sudo cp systemd/pw-launcher.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now pw-launcher

systemctl status pw-launcher confirms it is running.

Next steps

Operator Quickstart | PodWarden Hub