# Manager installation

> Install and operate the Fleetdock Manager control plane with Docker Compose, TLS, persistent storage, and production configuration.

Source: https://fleetdock.dev/docs/manager


The **Fleetdock Manager** is the control plane — the dashboard and API. This page covers
installing it on a server, the environment variables it reads, and how to run it safely in
production. To run it only on your own computer instead, see
[Install on your computer](/docs/local).

<Callout type="info">
  **Two installers, two jobs.** `fleetdock.dev/install.sh` installs the Manager, once. The
  command from **Servers → Connect server** installs the [Agent](/docs/agents) on each
  database server — it is served by your own Manager, not by fleetdock.dev.
</Callout>

## System requirements [#system-requirements]

| Fleet size    | Suggested host                |
| ------------- | ----------------------------- |
| 1–10 servers  | 2 vCPU, 4 GB RAM              |
| 10–50 servers | 4 vCPU, 8 GB RAM              |
| 50+ servers   | Dedicated database + API host |

You also need Docker Engine 24+ and &#x2A;*Docker Compose v2.23+**, with ports 80 and 443 free.
TLS is handled for you by the bundled Caddy, which obtains a Let's Encrypt certificate
automatically — a domain pointed at the host is optional but recommended.

## Database requirements [#database-requirements]

The Manager stores metadata in **PostgreSQL**. The Compose stack bundles a local Postgres
container for development. In production, point `FLEETDOCK_DATABASE_URL` at managed
PostgreSQL (Neon, RDS, Cloud SQL, or any PostgreSQL 14+) with `sslmode=require`.

## Install [#install]

```bash
curl -sSL https://fleetdock.dev/install.sh | sudo sh
```

Run in a terminal, it asks where you are installing (a server, or only this computer), the
domain — press Enter for a free `<ip>.sslip.io` name with HTTPS — and the admin email. It
shows a summary and waits for you to confirm, then installs Docker if missing, writes
`/opt/fleetdock/{docker-compose.yml,.env}` with generated secrets, starts the stack, and
prints the dashboard URL, the bootstrap password and the next steps. Re-running it upgrades
in place without touching `.env`.

For automation (CI, cloud-init), pass the answers as flags and add `--yes`; without a
terminal it never asks anything:

```bash
curl -sSL https://fleetdock.dev/install.sh | sudo sh -s -- \
  --domain db.example.com --admin-email you@example.com --yes
```

| Option                 | Effect                                                                  |
| ---------------------- | ----------------------------------------------------------------------- |
| `--domain <host>`      | Dashboard hostname; gets an automatic Let's Encrypt certificate         |
| `--admin-email <e>`    | Bootstrap admin account                                                 |
| `--server` / `--local` | Skip the first question. `--local` is [only this computer](/docs/local) |
| `--port <n>`           | Local installs: serve on `http://localhost:<n>`                         |
| `-y`, `--yes`          | Ask nothing                                                             |
| `--no-tls`             | Plain HTTP on the host IP; no certificate                               |
| `--with-gateway`       | Also enable [external access](/docs/external-access)                    |
| `--tag <tag>`          | Image tag to run (default `latest`)                                     |
| `--dir <path>`         | Install directory (default `/opt/fleetdock`)                            |

Manage it afterwards with the `fleetdock` command it installs — `status`, `credentials`,
`logs`, `update`, `domain`, `gateway`, `doctor`, `backup-db`, `backup-config`,
`reset-admin-password`, `delete`. `fleetdock help` lists them all.

### From source [#from-source]

<Steps>
  <Step>
    ### Configure [#configure]

    ```bash
    git clone https://github.com/Fleetdock/fleetdock.git
    cd fleetdock
    cp .env.example .env
    ./scripts/generate-secrets.sh >> .env
    ```

    Set `FLEETDOCK_DOMAIN`, `FLEETDOCK_SITE_ADDRESS` and `FLEETDOCK_PUBLIC_URL` to your
    hostname.
  </Step>

  <Step>
    ### Start [#start]

    ```bash
    docker compose -f docker-compose.yml -f docker-compose.build.yml up --build -d
    ```

    Drop the overlay to run the published image instead of building it. For managed PostgreSQL,
    point `FLEETDOCK_DATABASE_URL` at it and scale the bundled one away with
    `--scale postgres=0`.
  </Step>
</Steps>

## Environment variables [#environment-variables]

Variables use the `FLEETDOCK_*` prefix. The most important ones:

| Variable                             | Default                                          | Notes                                                                                                         |
| ------------------------------------ | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `FLEETDOCK_DATABASE_URL`             | —                                                | Required. PostgreSQL connection string.                                                                       |
| `FLEETDOCK_ENV`                      | `development`                                    | `production` refuses to start with default secrets.                                                           |
| `FLEETDOCK_HTTP_ADDR`                | `:8080`                                          | Listen address for the API and dashboard.                                                                     |
| `FLEETDOCK_DOMAIN`                   | —                                                | The one hostname everything else is derived from.                                                             |
| `FLEETDOCK_SITE_ADDRESS`             | —                                                | Caddy site address. A bare host gets automatic TLS; `http://…` serves plain HTTP.                             |
| `FLEETDOCK_JWT_SECRET`               | dev default                                      | Set a strong secret in production.                                                                            |
| `FLEETDOCK_ENCRYPTION_KEY`           | dev default                                      | Encrypts credentials and storage keys at rest.                                                                |
| `FLEETDOCK_PUBLIC_URL`               | `http://localhost:8080`                          | URL agents and installers use to reach the API.                                                               |
| `FLEETDOCK_CORS_ORIGIN`              | `http://localhost:3000`                          | Only used for split-origin deployments; a normal same-origin install never hits CORS.                         |
| `FLEETDOCK_TRUST_PROXY_HEADERS`      | `false`                                          | Trust `X-Forwarded-For` for login rate limiting. Set only when the API is reachable *solely* through a proxy. |
| `FLEETDOCK_HEARTBEAT_TIMEOUT`        | `2m`                                             | No heartbeat for this long ⇒ node `offline`.                                                                  |
| `FLEETDOCK_METRICS_RETENTION`        | `168h`                                           | How long per-heartbeat metrics are kept.                                                                      |
| `FLEETDOCK_AUDIT_RETENTION`          | `8760h`                                          | How long [audit log](/docs/audit-log) events are kept (365 days). `0` keeps them forever.                     |
| `FLEETDOCK_GATEWAY_ENABLED`          | `false`                                          | Enable the Fleetdock Gateway (HAProxy) service.                                                               |
| `FLEETDOCK_GATEWAY_PUBLIC_HOST`      | `FLEETDOCK_DOMAIN`                               | Hostname external apps use to reach the gateway. Defaults to your main domain.                                |
| `FLEETDOCK_GATEWAY_PORT_RANGE_START` | `15432`                                          | First port in the gateway's public port range.                                                                |
| `FLEETDOCK_GATEWAY_PORT_RANGE_END`   | `15481`                                          | Last port in the gateway's public port range.                                                                 |
| `FLEETDOCK_GATEWAY_DIAG_PORT`        | `15431`                                          | Reports the source address the gateway observes. `0` disables.                                                |
| `FLEETDOCK_GATEWAY_SOURCE_IP_MODE`   | `direct`                                         | `direct` or `proxy-protocol` (behind an L4 load balancer).                                                    |
| `FLEETDOCK_GATEWAY_CONFIG_PATH`      | `/var/lib/fleetdock/gateway/haproxy.cfg`         | HAProxy config path.                                                                                          |
| `FLEETDOCK_GATEWAY_MASTER_SOCKET`    | `/var/lib/fleetdock/gateway/haproxy-master.sock` | HAProxy master socket for hitless reload.                                                                     |
| `FLEETDOCK_GATEWAY_ADMIN_SOCKET`     | `/var/lib/fleetdock/gateway/haproxy-admin.sock`  | HAProxy stats socket, used to read endpoint health.                                                           |

<Callout type="info">
  There is no API URL to configure for the dashboard. It calls the API on
  whatever origin serves it, so the published image works unmodified on any
  hostname and changing your domain never requires a rebuild.
</Callout>

See `.env.example` in the repository for the full list, including optional SMTP variables
for email notifications and gateway configuration for [external database access](/docs/external-access).

## HTTPS and reverse proxy [#https-and-reverse-proxy]

The stack ships Caddy on 80/443 with automatic certificates, so there is nothing to set up.
Nothing else is published — the control plane's 8080 stays on the internal network.

If you replace it with your own proxy, there is no path routing to reproduce: the control
plane serves the API and the dashboard on one port, so a single upstream is the whole
config.

```text
db.example.com {
    encode zstd gzip
    reverse_proxy fleetdock:8080
}
```

<Callout type="warn">
  Keep `FLEETDOCK_TRUST_PROXY_HEADERS=true` only while the API is reachable
  *solely* through the proxy. If port 8080 is also exposed, a client can spoof
  `X-Forwarded-For` and bypass the login rate limiter.
</Callout>

## Persistent storage [#persistent-storage]

The bundled Postgres writes to a Docker named volume (`postgres_data`). In production, your
managed PostgreSQL provider is the source of truth — the metadata database holds users,
server enrollment, encrypted credentials, backup destinations, and job history. Losing it
without a backup means re-enrolling every server and re-entering every external credential.

## Verify [#verify]

```bash
curl -fsS https://db.example.com/healthz    # {"status":"ok"}
curl -fsS https://db.example.com/readyz     # {"status":"ready"} once the DB is up
```

Then sign in and change the bootstrap admin password on the **Profile** page. Interactive
API docs are also available on this site under [API reference](/docs/api) (with an
interactive playground). The Manager itself serves Redoc at `/docs` over the embedded
OpenAPI spec.

## Update [#update]

```bash
fleetdock update              # latest
fleetdock update --tag v0.2.0 # a specific release
```

`fleetdock update` backs up the metadata database first and stops if that fails.

Or, from a source checkout:

```bash
git fetch origin && git checkout v0.2.0
docker compose -f docker-compose.yml -f docker-compose.build.yml up --build -d
```

Migrations run automatically on API start. Confirm `GET /readyz` returns `ready`, then spot
-check login and a connection test. Watch the **Operations** page for stuck jobs after an
upgrade.

<Callout type="warn">
  Before upgrading, back up the metadata PostgreSQL database. If a migration has
  already advanced the schema, roll back by restoring that database, not just
  the API binary.
</Callout>

## Uninstall [#uninstall]

```bash
fleetdock uninstall   # stop and remove containers; data and configuration stay
fleetdock delete      # remove everything: containers, volumes, images, configuration
```

<Callout type="error">
  `fleetdock delete` permanently removes all Fleetdock metadata and the `.env` that holds
  `FLEETDOCK_ENCRYPTION_KEY`. It lists what it will remove and asks you to type `DELETE`.
  Run `fleetdock backup-config` first if you might rebuild. A source checkout run with
  `docker compose` is removed with `docker compose down -v`.
</Callout>
