# Docker installation

> Install FeatherPanel with Docker Compose, by the installer script or by hand. Covers the services, volumes, ports, .env values and the first start.

Source: https://featherpanel.com/docs/panel/installation/docker/

## With the installer

Run the installer as root and choose `[1] Panel`, then `Install Panel`, then Docker mode:

```bash
curl -sSL https://get.featherpanel.com/installer.sh | bash
```

The installer then:

1. Updates the system and installs `curl`, `unzip`, `jq` and `socat`, and Docker if it is missing.
2. Creates `/var/www/featherpanel` and `/var/www/featherpanel/backups`.
3. Writes a random `DOCKER_UPDATER_TOKEN` to `/var/www/featherpanel/.env` and installs the
   `featherpanel-docker-updater` systemd service (see [Updating](https://featherpanel.com/docs/panel/updating)).
4. Downloads `docker-compose.yml` from the `main` branch (or `docker-compose.v2.dev.yml` for development builds) and
   applies the panel port.
5. Stops any old FeatherPanel containers and runs `docker compose up -d`.
6. Sets up the access method you picked: a Cloudflare Tunnel, or an nginx or Apache site from the templates in
   [Web server](https://featherpanel.com/docs/panel/web-server) with an optional Let's Encrypt certificate.
7. Installs the `featherpanel` command in `/usr/local/bin`, which runs `php cli` inside the backend container. See
   [Panel CLI](https://featherpanel.com/docs/cli/panel).

Wait about five minutes, open the panel and register. The first account becomes the administrator.

### Unattended installs

Every menu choice can be passed as an environment variable. The full list is in the
[installer reference](https://featherpanel.com/docs/cli/installer). For example:

```bash
curl -sSL https://get.featherpanel.com/installer.sh | env FP_COMPONENT=panel FP_ACTION=install bash
```

## By hand

You can run the same Compose file without the installer.

```bash
mkdir -p /var/www/featherpanel && cd /var/www/featherpanel
curl -fsSL -o docker-compose.yml https://raw.githubusercontent.com/MythicalLTD/FeatherPanel/main/docker-compose.yml
```

Create `/var/www/featherpanel/.env` before the first start. Compose reads it for the variables below; every one has a
default in the Compose file.

```ini
MARIADB_ROOT_PASSWORD=change-me
MARIADB_DATABASE=featherpanel
MARIADB_USER=featherpanel
MARIADB_PASSWORD=change-me-too
REDIS_PASSWORD=change-me-three
FEATHERPANEL_PANEL_PORT=4831
```

> [!WARNING]
> The `mysql` health check in `docker-compose.yml` runs `mariadb-admin ping` with the literal password
> `featherpanel_root`. If you change `MARIADB_ROOT_PASSWORD`, change the `-p` argument of that health check too, or the
> database never reports healthy and the other services never start.

> [!NOTE]
> MariaDB only reads `MARIADB_*` values when it initialises an empty volume. Changing them later does not change the
> passwords of an existing database.

Start the stack:

```bash
docker compose up -d
docker compose ps
```

## What runs

| Service | Image | Role |
|---------|-------|------|
| `mysql` | `mariadb:latest` | Database. Not published on the host. |
| `redis` | `redis:latest` | Cache, sessions and queues, with append-only persistence and a password. |
| `backend` | `featherpanel-backend` | PHP API under FrankenPHP (worker mode by default) and the cron runner, both managed by supervisord. |
| `frontendv2` | `featherpanel-frontendv2` | Next.js frontend. Publishes `${FEATHERPANEL_PANEL_PORT:-4831}:80` and proxies `/api` to the backend. |
| `mcp` | `featherpanel-mcp` | [MCP server](https://featherpanel.com/docs/panel/mcp-server) on port 3001 inside the network. |
| `async-runner` | `featherpanel-async-runner` | Rust worker for background jobs. See [Cron and workers](https://featherpanel.com/docs/panel/cron-and-workers). |

All images are published for `amd64` and `arm64`. On 32-bit ARM the installer sets up QEMU emulation.

Only the frontend publishes a port. The backend reaches the host's updater service through `host.docker.internal:9418`.

## Volumes

| Volume | Mounted at | Contents |
|--------|-----------|----------|
| `mariadb_data` | `/var/lib/mysql` | The database |
| `redis_data` | `/data` | Redis append-only file |
| `featherpanel_config` | `/var/www/html/storage/config` | The backend `.env`, including `DATABASE_ENCRYPTION_KEY` |
| `featherpanel_attachments` | `/var/www/html/public/attachments` | Uploaded files |
| `featherpanel_snapshots` | `/var/www/html/storage/backups` | Panel snapshots |
| `featherpanel_logs` | `/var/www/html/storage/logs` | Logs, shared with the async runner |
| `featherpanel_translations` | `/var/www/html/public/translations` | Translation files |
| `featherpanel_addons` | `/var/www/html/storage/addons` | Installed plugins |
| `featherpanel_data` | `/var/www/html/storage/data` | Runtime data |
| `featherpanel_pma` | `/var/www/html/public/pma` | phpMyAdmin files |
| `featherpanel_webmail` | `/var/www/html/public/webmail` | Webmail files |
| `mcp_oauth_data` | `/data` | MCP OAuth store |

> [!DANGER]
> `featherpanel_config` holds the key that encrypts secrets in the database. A database backup without this volume cannot
> decrypt them. Back up both. See [Backups](https://featherpanel.com/docs/panel/backups).

Compose prefixes volume names with the project name, so on an installer host they appear as
`featherpanel_mariadb_data`, `featherpanel_featherpanel_config` and so on.

## Backend environment

The Compose file sets these on the backend container. You rarely need to change them.

| Variable | Value | Meaning |
|----------|-------|---------|
| `DATABASE_HOST`, `DATABASE_PORT` | `mysql`, `3306` | Database inside the Compose network |
| `DATABASE_ENCRYPTION` | `xchacha20` | Cipher for encrypted columns |
| `TRUST_PROXY_HEADERS` | `true` | Read the client IP from `X-Forwarded-For` |
| `PANEL_DOCKER_DEPLOYMENT` | `true` | Enables Docker-only actions such as the in-panel update button |
| `FRANKENPHP_WORKER` | `true` | Set `false` for classic (non-worker) PHP mode |
| `DOCKER_UPDATER_URL`, `DOCKER_UPDATER_TOKEN` | `http://host.docker.internal:9418/update`, from `.env` | Host updater used by the update button |

The full list of variables the backend reads is in [Environment variables](https://featherpanel.com/docs/panel/environment-variables).

## Check that it works

```bash
cd /var/www/featherpanel
docker compose ps
curl -fsS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:4831/
```

All services should show `healthy`. If the backend restarts in a loop, read its log with `docker compose logs backend`
and see [Troubleshooting](https://featherpanel.com/docs/troubleshooting).

## Frequently asked questions

### Which port does FeatherPanel use?

The frontend container publishes port 4831 on the host by default. Set FEATHERPANEL_PANEL_PORT in /var/www/featherpanel/.env, or the panel port in the installer's Configuration menu, to change it.

### Where does the Docker install live?

The installer keeps docker-compose.yml, .env, install.log and the backups folder in /var/www/featherpanel. Application data lives in named Docker volumes whose names start with featherpanel_.

### How long does the first start take?

The installer asks you to wait about five minutes. The backend health check allows a 180 second start period while it runs database migrations.
