Skip to main content

All-In-One Docker Compose Install

This install runs Postgres, the Wacht server, three local probes, and the web UI using published GHCR images.

Requirements

  • Docker
  • Docker Compose
  • A domain and reverse proxy if you want public HTTPS access

Create Files

This block creates the directory, generates the required secrets, downloads the Compose example, and starts the stack.

# Create the working directory.
mkdir wacht
cd wacht

# Generate local credentials.
POSTGRES_PASSWORD="$(openssl rand -hex 24)"
SEED_USER_EMAIL="you@example.com"
SEED_USER_PASSWORD="$(openssl rand -hex 18)"
PROBE_1_SECRET="$(openssl rand -hex 32)"
PROBE_2_SECRET="$(openssl rand -hex 32)"
PROBE_3_SECRET="$(openssl rand -hex 32)"

# Write the values consumed by compose.yaml.
cat > .env <<EOF
POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
SEED_USER_EMAIL=${SEED_USER_EMAIL}
SEED_USER_PASSWORD=${SEED_USER_PASSWORD}
PROBE_1_SECRET=${PROBE_1_SECRET}
PROBE_2_SECRET=${PROBE_2_SECRET}
PROBE_3_SECRET=${PROBE_3_SECRET}
WACHT_WEB_PORT=127.0.0.1:3000
EOF

# Download the example stack.
curl -fsSL https://wacht.cloud/examples/compose.yaml \
-o compose.yaml

# Keep the first login credentials locally.
cat > credentials.txt <<EOF
Admin email: ${SEED_USER_EMAIL}
Admin password: ${SEED_USER_PASSWORD}
EOF

# Restrict files containing secrets.
chmod 600 .env credentials.txt

# Start Wacht.
docker compose up -d

The seed user is only created on first boot when no users exist yet. .env and credentials.txt contain secrets; do not commit or share them.

Private Targets

The example enables private targets because self-hosted probes often monitor Docker, VPN, LAN, or other RFC1918 services.

Disable allow_private_targets on both the server and probes when probes should only reach public destinations.

Open:

http://localhost:3000

The web container proxies /api, /status, and /healthz to the server. The server is only exposed inside the Compose network.

Public HTTPS

The example binds the web UI to 127.0.0.1:3000. Put Caddy or Nginx in front:

wacht.example.com {
reverse_proxy 127.0.0.1:3000
}

Let the reverse proxy handle TLS on ports 80 and 443. Do not expose Postgres publicly.

Verify

Health check:

curl http://127.0.0.1:3000/healthz

Login API:

ADMIN_EMAIL="$(awk -F': ' '/Admin email/ {print $2}' credentials.txt)"
ADMIN_PASSWORD="$(awk -F': ' '/Admin password/ {print $2}' credentials.txt)"

curl -s -X POST http://127.0.0.1:3000/api/auth/login \
-H 'Content-Type: application/json' \
-d "{\"email\":\"${ADMIN_EMAIL}\",\"password\":\"${ADMIN_PASSWORD}\"}"

After verifying the install, sign in and change the generated password.

Stop

Stop containers and keep data:

docker compose down

Stop containers and remove the database volume:

docker compose down -v

Use down -v only when you intentionally want a clean slate.