# Install the daemon

Source: https://www.pingtower.com/docs/self-hosting/install/

A single static Go binary on a Linux box, verified before it runs, under systemd, with its data in one directory of SQLite files.

<div class="docs-steps">


1. **Download and verify**

   Get the binary for your architecture from the [download table](/get-started/#self-host). Every release carries an SSH signature over a signed provenance file that names exactly what was built. Check the SHA-256 against the table, then verify the signature against the release public key, [`pingtower-release.pub`](/get-started/#self-host).

   ```sh
   sha256sum pingtower-linux-amd64   # compare with the table
   printf 'release@pingtower.com namespaces="pingtower-release" %s\n' \
     "$(cut -d' ' -f1,2 pingtower-release.pub)" > allowed_signers
   ssh-keygen -Y verify -f allowed_signers -I release@pingtower.com \
     -n pingtower-release -s pingtower-linux-amd64.release.sig \
     < pingtower-linux-amd64.release
   cat pingtower-linux-amd64.release   # sha256= must match the sum above
   ```

   Needs stock OpenSSH 8.2 or later. If any check fails, do not run the binary.

2. **Install the binary and a user**

   ```sh
   sudo install -m 0755 pingtower-linux-amd64 /usr/local/bin/pingtower
   sudo useradd --system --home /var/lib/pingtower --shell /usr/sbin/nologin pingtower
   sudo mkdir -p /var/lib/pingtower
   sudo chown pingtower:pingtower /var/lib/pingtower
   ```

3. **Create a systemd unit**

   `/etc/systemd/system/pingtower.service`:

   ```ini
   [Unit]
   Description=pingtower
   After=network-online.target
   Wants=network-online.target

   [Service]
   Type=simple
   User=pingtower
   Group=pingtower
   ExecStart=/usr/local/bin/pingtower \
     -listen 127.0.0.1:8391 \
     -data-dir /var/lib/pingtower
   Restart=on-failure
   RestartSec=2
   TimeoutStopSec=20
   LimitNOFILE=65536
   UMask=0077
   NoNewPrivileges=yes
   ProtectSystem=strict
   ReadWritePaths=/var/lib/pingtower
   ProtectHome=yes
   PrivateTmp=yes

   [Install]
   WantedBy=multi-user.target
   ```

   ```sh
   sudo systemctl daemon-reload
   sudo systemctl enable --now pingtower
   curl -s http://127.0.0.1:8391/healthz
   ```

4. **Put TLS in front**

   The daemon speaks plain HTTP on localhost and expects a reverse proxy to terminate TLS. See [Reverse proxy & TLS](/docs/self-hosting/proxy/).

5. **First account, first tenant**

   ```sh
   curl -s https://alerts.example.com/v1/signup \
     -H 'Content-Type: application/json' \
     -d '{"email":"you@example.com","password":"…"}'
   # → {"account":{…},"token":"pts_…"}

   curl -s https://alerts.example.com/v1/tenants \
     -H "Authorization: Bearer pts_…" \
     -H 'Content-Type: application/json' \
     -d '{"name":"Acme"}'
   ```

   From here the [quickstart](/docs/getting-started/quickstart/) applies unchanged, with your domain in place of api.pingtower.com.


</div>


## Useful flags

`pingtower -h` lists everything. The ones most boxes set:

| Flag | Does |
| --- | --- |
| `-listen` | Address to serve on. Keep it on localhost behind the proxy. |
| `-data-dir` | Where `control.db` and `tenants/<id>/pingtower.db` live. One directory, back it up as a unit. |
| `-retain-days` | How long retained log rows are kept. |
| `-max-logs-per-source` | A per-source cap on retained rows. |
| `-backup-dir` | Where periodic SQLite backups are written. |
| `-secret-key` | Seals integration credentials at rest with AES-256-GCM. Set it before creating any integration. |

## Storage

One `control.db` holds accounts, tenants, memberships, sessions, API keys and invites. Each tenant gets its own `tenants/<tenant_id>/pingtower.db` with that tenant's sources, templates, retained logs, alerts and project configs. Tenant isolation falls out of which file was opened, chosen by the caller's credential. SQLite runs in WAL mode with a single writer per file, so the data directory copies cleanly from a backup.

## Upgrading

Replace the binary and restart the unit. Schema migrations run at startup. Read the release notes on the [blog](/projects/go/) first; a release that needs anything more says so there.
