# Install

> Run Telesphorepo in a container or as a single binary.

Source: https://docs.telesphorepo.com/getting-started/install/

Telesphorepo needs no database server and no required environment variable. You can run it in a container or as a single binary.

## Run in a container

The image is `ghcr.io/telesphorepo/telesphorepo`. It supports `linux/amd64` and `linux/arm64`. The `latest` tag follows the newest release, and each release also has `X.Y.Z` and `X.Y` tags.

- Docker

  ```sh
  docker run -d --name telesphorepo -p 8080:8080 -v telesphorepo-data:/data ghcr.io/telesphorepo/telesphorepo:latest
  docker logs telesphorepo
  ```

- Podman

  ```sh
  podman run -d --name telesphorepo -p 8080:8080 -v telesphorepo-data:/data ghcr.io/telesphorepo/telesphorepo:latest
  podman logs telesphorepo
  ```

The last command prints the first-start box with the generated `admin` password. See [First run](/getting-started/first-run/).

The container keeps all state in `/data`. Always mount a volume there. Without a volume, the database, the master key and the blobs vanish with the container.

The image runs as a non-root user (UID 65532). A named volume works as is. If you mount a host directory, make it writable for that UID.

To build the image yourself from a clone of the [source repository](https://github.com/Telesphorepo/Telesphorepo), run `go tool task build image`. It builds `telesphorepo:local`.

## Run the binary

Download the archive for your system from [GitHub Releases](https://github.com/Telesphorepo/Telesphorepo/releases). There are binaries for Linux, macOS and Windows, on amd64 and arm64. Check the download against `SHA256SUMS`, unpack it, and start the server:

```sh
./telesphorepo serve
```

The binary stores its state in `./data` by default. Set `TR_DATA_DIR` to put it elsewhere:

```sh
TR_DATA_DIR=/srv/telesphorepo ./telesphorepo serve
```

Run the binary under a service manager, such as systemd, so that it restarts after a reboot.

To build the binary from a clone, you need [Go](https://go.dev/dl/), [bun](https://bun.sh) and Node 26. Run `go tool task build`. It builds the web UI first and then embeds it in `bin/telesphorepo`. The build works the same on Windows, macOS and Linux.

## Environment variables

None are required. The server reads these three at boot, and a change needs a restart:

| Variable        | Default                           | What it does                                                         |
| --------------- | --------------------------------- | -------------------------------------------------------------------- |
| `TR_DATA_DIR`   | `./data` (`/data` in the image)   | Holds the database, the upload spool, local blobs and the master key |
| `TR_LISTEN`     | `:8080`                           | The listen address, for example `127.0.0.1:8080`                     |
| `TR_MASTER_KEY` | generated into the data directory | Key that encrypts secret settings. Base64, 32 bytes                  |

Two more variables, `TR_BOOTSTRAP_ADMIN_USER` and `TR_BOOTSTRAP_ADMIN_PASSWORD`, set the first admin account. See [First run](/getting-started/first-run/#choose-the-first-admin-yourself).

You can also force any other setting from the environment as `TR_<KEY>`. See [Configuration](/getting-started/configuration/#override-a-setting-from-the-environment).

### Keep the master key off the volume

By default the server writes a master key to `master.key` in the data directory. A backup of the data directory then contains the key that decrypts your secret settings.

To keep the key elsewhere, generate one and pass it in from a secret manager:

```sh
openssl rand -base64 32
```

```sh
docker run -d --name telesphorepo -p 8080:8080 \
  -v telesphorepo-data:/data \
  -e TR_MASTER_KEY="$(cat /run/secrets/tr_master_key)" \
  ghcr.io/telesphorepo/telesphorepo:latest
```

Set `TR_MASTER_KEY` before the first start. See [Lost master key](/getting-started/configuration/#lost-master-key) for what happens if the key changes later.

## Put a reverse proxy in front

The server speaks plain HTTP. Run a reverse proxy in front of it for HTTPS. This Caddy file is a complete example:

```text
packages.example.com {
    reverse_proxy 127.0.0.1:8080
}
```

Check these points when you add a proxy:

- **Body size.** Do not let the proxy cap request bodies below `max_upload_bytes`. The default is 2 GiB. Some proxies default to 1 MiB.
- **Client IP.** The server rate-limits failed sign-ins by client IP. It reads `X-Forwarded-For` only from peers listed in the `trusted_proxies` setting. Add the CIDR of your proxy, for example `172.17.0.0/16`. Without it, every request seems to come from the proxy.
- **Session cookie.** The session cookie is `Secure` by default, so the UI needs HTTPS. If you serve plain HTTP on a non-local host, set `TR_COOKIE_SECURE=false`. Maven 3.8.1 and later also block `http://` repositories on non-local hosts.
- **Public URL.** Set `base_url` to the address that clients use. See the next section.

## Set the public URL

Open **Settings** and set **Public base URL** (`base_url`) to the address that clients use:

```text
https://packages.example.com
```

The server uses `base_url` for:

- the client snippets in the UI;
- the approval link that a client prints when you enroll a device;
- the redirect URL for single sign-on, unless you set `oidc.redirect_url`;
- the default audience of CI [trust policies](/security/trust-policies/);
- passkeys. Passkeys do not work until `base_url` is set.

> Caution
>
> Set `base_url` before you register passkeys. The browser binds a passkey to the host of `base_url`. If the host changes later, the registered passkeys stop working. Authenticator-app codes are not affected.

## Check that it runs

```sh
curl http://localhost:8080/healthz
```

The answer is `ok` when the database responds. Use this URL for a container health check or a proxy upstream check.

## Next

Follow [First run](/getting-started/first-run/) to sign in.
