Install

From zero to a running node in one command

Docker Compose is the recommended install: one command starts the control plane, embedded agent and PostgreSQL. The script install remains available for hosts managed by systemd.

Run One command Modes all-in-one · server · agent Platform Linux amd64 / arm64 Time ~2 minutes
bash
curl -fsSL https://raw.githubusercontent.com/shuaiZend/PingWAF/main/deploy/compose/install.sh | bash

Command builder

Compose your install command

Docker Compose is pre-selected — one command, database included. Fields are pre-filled with the documented defaults; change only what you need. Script install remains on the second tab.

Docker Compose

Deploy with Docker Compose

Docker Compose is the recommended path: one command starts the control plane, its embedded agent and PostgreSQL, with data and certificates in named volumes. Upgrades only change the image tag.

  • Docker Engine 24+ with the Compose v2 plugin (docker compose, not the legacy docker-compose binary).
  • Ports 80, 443, 9080 and 9090 free on the host — or override them in the fields below.
  • No external database needed: the compose file ships its own PostgreSQL 16 container with named volumes.
  • The dashboard serves HTTPS with a self-signed certificate on first boot. Upload a real one under Settings → Control plane HTTPS, or set TLS_ENABLED=false for plain HTTP.
One-line install
curl -fsSL https://raw.githubusercontent.com/shuaiZend/PingWAF/main/deploy/compose/install.sh | bash

Run it in an empty directory: the installer downloads docker-compose.yml, writes a private .env, pulls the image and starts the stack. An existing .env is never overwritten — re-run the same command to upgrade.

  1. 1

    Run the one-liner

    Run the command above in an empty directory. The installer creates ./pingwaf, downloads the compose file and writes a private .env.

  2. 2

    Wait for the stack

    Compose pulls the image, starts PostgreSQL, waits for its health check and then starts PingWAF. The first boot takes a minute or two.

  3. 3

    Sign in

    Open https://<host>:9080 and use ADMIN_EMAIL / ADMIN_PASSWORD (the default password must be changed at first sign-in). Then add a site.

Need to customise? Generate a command with flags

Only change what you need; the command appends flags that differ from the defaults. The administrator password is never written into the command — the console forces a change at first sign-in.

Start the stack
curl -fsSL https://raw.githubusercontent.com/shuaiZend/PingWAF/main/deploy/compose/install.sh | bash

First login: https://<your-host>:9080 with the ADMIN_EMAIL / ADMIN_PASSWORD above (accept the self-signed certificate warning until you upload a real one). Agents reach the control plane on :9090.

Upgrading

Pulls the image for the tag in your .env and recreates the container with the same volumes — configuration, data and certificates survive the restart. Pin the tag when you want controlled upgrades.

bash
docker compose pull && docker compose up -d
Terminal showing docker compose up -d creating the network and volumes, starting PostgreSQL and PingWAF and printing the dashboard URL
docker compose up -d pulls the image, starts PostgreSQL, waits for it to become healthy and brings the dashboard up.

Pre-filled with the installer defaults — only values you change are added to the command.

Your install command
curl -fsSL https://raw.githubusercontent.com/shuaiZend/PingWAF/main/deploy/compose/install.sh | bash

Only values that differ from the defaults are appended as flags, so the untouched builder produces the shortest one-liner. Arguments reach the piped script through bash -s --.

Interactive setup

Or let the installer ask

Run the script from a terminal without flags and it walks through mode, credentials, database and certificate names, then prints the resolved plan for confirmation.

  1. Choose the deployment mode

    1 all-in-one, 2 server or 3 agent — press Enter to accept the bracketed default.

  2. Fill in the parameters

    Agents ask for the control plane URL and enrollment key; server modes ask for the administrator account, PostgreSQL URL and certificate names. Secrets stay hidden while typed.

  3. Confirm the summary

    Before anything is written, the installer prints the resolved plan — mode, version, platform, paths, systemd state and a masked database URL.

  4. Finish and verify

    Server modes print the start command; agents are enabled and started automatically. Confirm the node under Nodes in the dashboard.

Interactive mode needs a local terminal. Under curl | bash stdin is the script itself — download it first, then run bash install.sh to answer the prompts.
Interactive installer prompting for mode, administrator account, PostgreSQL URL and certificate names, ending in the installation plan
The interactive installer prompts in order and prints the resolved plan before writing anything.
Installer success banner listing binary, config, data and service paths plus next steps
A successful run registers pingwaf.service and prints where everything landed.

Flag reference

Installer flags at a glance

Every flag install.sh accepts. Each one also has a PINGWAF_* environment variable equivalent.

--mode

Operating mode: all-in-one, server or agent. Defaults to all-in-one.

--version

Install a specific release (a leading v is stripped). Empty or 'latest' resolves the newest GitHub release.

--db-url

PostgreSQL connection string for server/all-in-one. Defaults to postgres://pingwaf:pingwaf@localhost:5432/pingwaf.

--server-url

Control plane gRPC URL for agent mode. Use https:// — gRPC TLS is served by default.

--api-key

Agent enrollment token. Can also be supplied as PINGWAF_API_KEY to keep it off the command line.

--admin-email

Seeded administrator e-mail (server/all-in-one). Defaults to admin@pingwaf.local.

--admin-password

Seeded administrator password (server/all-in-one). Empty keeps pingwaf123, which the console forces you to change at first sign-in.

--tls-sans

Comma-separated hostnames the generated dashboard certificate should cover.

--no-systemd

Skip creating the systemd unit — for containers, upgrades or hand-rolled process managers.

--interactive / --non-interactive

Force prompting even when flags are given, or never prompt and use the given values plus defaults. Piping without flags stays non-interactive.

--dry-run

Print the resolved plan — mode, version, platform, paths — and exit without installing.

CLI handbook on GitHub

Troubleshooting

When the install does not come up clean

The five failures seen most often in support, and the command that pinpoints each one.

The installer cannot reach PostgreSQL

all-in-one and server modes need PostgreSQL 14+ before first boot. The script warns when psql cannot connect — install the server, create the pingwaf user and database (createuser / createdb), then re-run the installer or edit /etc/pingwaf/pingwaf.toml and run systemctl restart pingwaf.

bind() failed on 0.0.0.0:80 or :443

The data plane binds 80/443 as the unprivileged pingwaf user, which only works because the unit grants CAP_NET_BIND_SERVICE. If binding still fails, check systemctl show pingwaf -p AmbientCapabilities and add the capability with a drop-in: sudo systemctl edit pingwaf → [Service] AmbientCapabilities=CAP_NET_BIND_SERVICE. Units written before the capability was granted keep failing until the drop-in is added.

The binary fails with a versioned glibc symbol error

Release assets are built on Ubuntu 24.04 and need glibc 2.39 or newer. On older distributions (Debian 11, Ubuntu 20.04, CentOS/RHEL, Alpine, …) the installer's smoke test stops with a clear message — build from source instead.

The systemd service will not start

Inspect the unit log with journalctl -u pingwaf -e and review /etc/pingwaf/pingwaf.toml. There is no reload: apply any edit with systemctl restart pingwaf and check systemctl status pingwaf afterwards.

Container startup fails with "bind() failed on 0.0.0.0:443" / Permission denied

The image already grants CAP_NET_BIND_SERVICE to the binary at build time, so the unprivileged pingwaf user can bind 80/443 in bridge mode with plain docker run or docker compose up -d. If it still fails, the runtime stripped file capabilities — add --cap-add=NET_BIND_SERVICE (the bundled compose file already sets cap_add), or switch to host networking with network_mode: host (then comment out ports: and networks:; 9080/9090 will listen on the host directly). Verify with docker exec pingwaf getcap /usr/local/bin/pingwaf (expect cap_net_bind_service=ep) and docker exec pingwaf id -u (non-zero). Full details: https://github.com/shuaiZend/PingWAF/blob/main/docs/deployment.md#binding-ports-80443-in-containers

Installed? Now add your first site.

The docs cover the site model, distributed topologies, configuration precedence and the full environment variable reference.