# Deploy an outpost

Run the outpost on your server or NAS to receive notifications from
selected email folders. You need Docker, plus Docker Compose v2 for
the recommended Compose installation.

**Image availability**: available versions are listed
in
[GitHub Releases](https://github.com/mingzaily/mailwake/releases)
and
[GHCR](https://github.com/mingzaily/mailwake/pkgs/container/mailwake).

## 1. Choose an installation method

**Docker Compose is recommended**: installation,
updates and backups use the same configuration. The image is
`ghcr.io/mingzaily/mailwake:latest`, for Linux amd64 /
arm64. `latest` follows the stable release. To pin a version, replace
`latest` with a published tag from Releases.

In your server terminal:

```
mkdir -p ~/mailwake
cd ~/mailwake
```

Create `compose.yaml` in that directory with this
complete configuration:

```
services:
  core:
    image: ghcr.io/mingzaily/mailwake:latest
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:8080"
    environment:
      MAILWAKE_LISTEN: "0.0.0.0:8080"
      MAILWAKE_DATA_DIR: "/data"
      MAILWAKE_RELAY_URL: "${MAILWAKE_RELAY_URL:-}"
    volumes:
      - core-data:/data
    read_only: true
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    stop_grace_period: 30s
volumes:
  core-data:
```

Pull the image and start the outpost:

```
docker compose pull core
docker compose up -d
docker compose ps
docker compose logs --tail=50 core
```

**Check the result**: `core` shows
`Up` / `running`, and the logs contain the
initial setup code. Keep the terminal available for the next step.
Run future Compose commands from `~/mailwake`.

The `core-data` Docker volume stores `/data`.
With this directory name, its default Docker name is
`mailwake_core-data`. Recreating the container preserves
it; `docker compose down -v` deletes the volume and its
contents.

### Docker Run

Choose this equivalent installation if you prefer a single command.
Use one installation method for your instance.

```
docker volume create mailwake-data
docker run -d \
  --name mailwake \
  --restart unless-stopped \
  --stop-timeout 30 \
  --read-only \
  --security-opt no-new-privileges:true \
  --cap-drop ALL \
  -p 127.0.0.1:8080:8080 \
  -v mailwake-data:/data \
  ghcr.io/mingzaily/mailwake:latest
docker logs --tail=50 mailwake
```

This installation uses the `mailwake-data` volume. The
following management commands use Compose; see the
[Docker Run backup instructions](backup.md#back-up-a-docker-run-installation)
for the alternative.

## 2. Open the console

**On the Docker host**: open
<http://127.0.0.1:8080>.

**On a remote server or NAS**: run this command on your
own computer, replacing `user@server` with your
server's SSH user and address:

```
ssh -L 18080:127.0.0.1:8080 user@server
```

Keep the connection open and visit
<http://127.0.0.1:18080> on your
computer. This is enough to complete initial setup. Configure HTTPS
below when you want ongoing access through a domain.

**Check the result**: the Mailwake setup screen opens.
Enter the setup code from the logs and create an administrator
password of at least 12 characters. Restarting the outpost before setup
generates a new setup code.

Continue with
[your first notification](first-notification.md)
to connect a mailbox and a notification channel.

## 3. Configure HTTPS

Point your domain to the server and configure your reverse proxy to
forward to `http://127.0.0.1:8080`. For Caddy installed
on the same host, add your domain to `Caddyfile`:

```
mail.example.org {
    reverse_proxy 127.0.0.1:8080
}
```

Verify DNS and allow inbound ports 80 / 443, then validate and
reload:

```
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
```

Open `https://mail.example.org`, confirm the certificate
is valid, and check that you can sign in and open a mailbox. See the
[official Caddy installation guide](https://caddyserver.com/docs/install)
to install Caddy. In a proxy panel such as Nginx Proxy Manager,
enter the domain and forwarding address and request a certificate in
the panel.

For a proxy in another container, join it to the outpost's Docker
network and forward to `http://core:8080`; its
`127.0.0.1` refers to itself. Preserve the original Host
and set `X-Forwarded-For`. The outpost accepts proxy information
from loopback and private networks; expose port 8080 only to the
proxy and trusted local clients.

## 4. Update the outpost

Complete a [backup](backup.md), then run in
the Compose directory:

```
docker compose pull core
docker compose up -d
docker compose ps
docker compose logs --tail=50 core
```

For a pinned version, change the image tag in
`compose.yaml` first. Open the console, check mailbox
monitoring, and send a new email to verify delivery. Keep the
pre-update backup. To roll back, restore both its data and its
matching image using the restore tutorial.

## 5. Troubleshooting

### The image cannot be pulled

Check whether GHCR contains the requested version.
`manifest unknown` usually means a missing tag. Check the
image address and package visibility for `denied` errors,
or server connectivity to GHCR for timeouts.

### The console will not open

`127.0.0.1` refers to the computer you are using. Use the
SSH tunnel or an HTTPS domain for a remote installation. Check
`docker compose ps` and
`docker compose logs --tail=50 core` first. If 8080 is
occupied, change the mapping to
`127.0.0.1:18080:8080` and use host port 18080 in the
browser and reverse proxy.

### Reset an administrator password

Run in the Compose directory and enter the new password at the
terminal prompt:

```
docker compose stop core
docker compose run --rm -it core admin reset-password
docker compose start core
```

The recovery command acquires the data directory lock, so stop the outpost
first. Resetting the password revokes browser sessions and preserves
API tokens, mailboxes and the queue.

### Connect the official App and Pro

The Free Web console and Bark / Pushover / Webhook delivery work
independently. For the official App, follow
[App integration](connect-app.md)
for HTTPS and pairing.

Source builds and local development are covered in the
[development guide](https://github.com/mingzaily/mailwake/blob/main/docs/development.md).

## Source builds and process settings

For development builds, follow the [source repository README](https://github.com/mingzaily/mailwake#run-from-source) and [contributor build guide](https://github.com/mingzaily/mailwake/blob/main/CONTRIBUTING.md).

`MAILWAKE_LISTEN` defaults to `127.0.0.1:8080` outside Docker; the container uses `0.0.0.0:8080`. `MAILWAKE_DATA_DIR` defaults to `data`; the container uses `/data`. `MAILWAKE_RELAY_URL` defaults to `https://notify.mailwake.oritx.com`. Keep the complete data directory, including `secret.key`, across restarts and upgrades.

## Change the push delivery environment

Production and Sandbox use separate pairing identities. To intentionally change environments, stop the outpost and run the reset against its existing data directory and saved address of the previous push delivery service:

```sh
docker compose stop core
docker compose run --rm -it core admin reset-native-push
```

Type `reset-native-push` when prompted. The command revokes pairings at the old push delivery service. If that service is unreachable, keep the old configuration and retry when it is reachable. After it confirms zero unresolved targets, set the new `MAILWAKE_RELAY_URL`, recreate the outpost with `docker compose up -d`, and [pair again](connect-app.md). The command preserves the outpost signing identity and ordinary-channel history.
