> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getpostern.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Install Postern

> Run Postern under Docker on a machine you own, then open its Console.

<Info>
  **Before you start**

  * **Docker.** [Install Docker](https://docs.docker.com/get-docker/) first. It is the only thing
    you install by hand.
  * **A machine that stays on.** While it is off, your agents reach nothing. Keep it updated, the
    same as anything else you run.
  * **About 10 minutes,** plus the time the image takes to download.
  * **Somewhere to keep one password.** You create it in [step 2](#password). Postern's database
    never accepts a different one afterwards, so open your password manager now.
  * **Windows: not tested.** Postern runs under Docker, so Windows should work. Nobody has run it
    there. Treat every Windows line on this page as untested.
</Info>

## Choose your path

| Postern will run on…                                                 | Then                                                                                                                 |
| -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| the computer in front of you                                         | Follow all four steps as written.                                                                                    |
| another computer — a NAS, a spare machine, a computer with no screen | Sign in to that computer over SSH. Run steps 1 to 3 there, then [forward the Console port](#open-console) in step 4. |

Postern runs either way. A NAS or a spare machine works the same as the computer in front of you.
The second path needs SSH on that computer, and nothing else changes.

<Steps>
  <Step title="Get the compose file" titleSize="h2" id="get-file">
    Open a terminal on the machine that will run Postern. On macOS, press **Command-Space**, type
    `Terminal`, press **Return**. On Windows, press the **Windows** key, type `PowerShell`, press
    **Return**.

    Make a folder and download the one file Postern needs:

    ```bash theme={"system"}
    mkdir postern && cd postern
    curl -O https://raw.githubusercontent.com/getpostern/postern/main/docker-compose.yml
    ```

    On Windows, type `curl.exe` — in PowerShell, `curl` means something else. Nobody has tested these
    lines:

    ```powershell theme={"system"}
    mkdir postern; cd postern
    curl.exe -O https://raw.githubusercontent.com/getpostern/postern/main/docker-compose.yml
    ```

    The folder now holds one file, `docker-compose.yml`. Every command below runs from this folder.

    You can clone the whole repository instead, if you have Git:
    `git clone https://github.com/getpostern/postern.git && cd postern`. Postern needs nothing else
    from it.
  </Step>

  <Step title="Write the database password" titleSize="h2" id="password">
    Postern's database needs one password. It lives in a file named `.env`, beside
    `docker-compose.yml`.

    <Warning>
      Postgres records this password the first time it starts. It never accepts a different one against
      the same data, and there is no way to change it afterwards. Keep `.env`. Do not type the password
      in front of `docker compose up`. That creates a new one on every run, and every other
      `docker compose` command then fails.
    </Warning>

    In that folder, run the line for your system. It makes a password of letters and digits only. A
    `/`, `:`, `@`, `?`, `#`, `&` or `+` breaks the address Postern uses to reach its database. Postern
    then exits at once. `openssl rand -base64 32` is not a substitute: its output holds those characters
    often.

    ```bash theme={"system"}
    printf 'POSTGRES_PASSWORD=%s\n' "$(openssl rand -hex 24)" > .env
    ```

    On Windows, run these four lines in PowerShell. Nobody has tested them:

    ```powershell theme={"system"}
    $b = New-Object byte[] 24
    [Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($b)
    $hex = [BitConverter]::ToString($b).Replace('-','').ToLower()
    Set-Content -Path .env -Value "POSTGRES_PASSWORD=$hex" -Encoding ascii
    ```

    `.env` now holds one line: `POSTGRES_PASSWORD=` and 48 letters and digits. That is the whole of
    Postern's required configuration. Every other setting has a working default.

    Copy that line into your password manager now. The leading dot makes `.env` invisible in Finder and
    File Explorer. Do not make `.env` in Notepad — Notepad adds `.txt` to the name.
  </Step>

  <Step title="Start Postern" titleSize="h2" id="start">
    Docker must be open first. On macOS and Windows, open the **Docker Desktop** application and wait
    for it to finish its own start-up. On Linux, Docker runs as a service and needs nothing opened.

    From the folder that holds `docker-compose.yml`, start Postern:

    ```bash theme={"system"}
    docker compose up -d
    ```

    The first run downloads the image `ghcr.io/getpostern/postern`, so it takes minutes. Later runs take
    seconds. The image runs on Intel and AMD machines, and on ARM machines such as Apple Silicon.
    Postern sets up its database tables before it answers anything, so port 8787 stays quiet for a few
    seconds after the command returns.

    Postern opens two ports to this computer only: **8787**, the Console port, and **8788**, the agent
    port. Only this computer can reach these addresses. Nothing on your Wi-Fi, and nothing on the
    internet, can. The database gets no port at all. Only Postern reaches it, over the private network
    Docker Compose creates.

    <Note>
      If another program already holds 8787 or 8788, the `app` container does not start. Add a line to
      `.env`, below the password line:

      ```
      REST_PORT=8790
      ```

      Then run `docker compose up -d` again. The Console now answers at `http://localhost:8790`, and
      every `8787` below becomes `8790`. `MCP_HTTP_PORT` moves the agent port the same way. If you
      already connected [Google](/connect/google), [Microsoft](/connect/microsoft) or
      [WHOOP](/connect/whoop), change the address you registered there to the new port. They match it
      exactly, character for character.
    </Note>

    <Warning>
      Docker keeps two storage areas outside the containers: your data, and the key that unlocks every
      account you connect. `docker compose down -v` deletes both, permanently. Every account you
      connected becomes unreadable, and nothing can recover them. `docker compose down` without `-v` is
      safe: it removes the containers and leaves both storage areas alone.

      Take a copy of the key: [Back up, export and erase](/operate/backup-and-export). If Postern cannot
      open the vault at start-up, it refuses to start rather than starting broken. A database restored
      without its key stops there.
    </Warning>

    <Note>
      **The `VAULT KEY IS NOT BACKED UP` notice.** Postern prints it in its own log at every start. It
      is a reminder, not an error, and Postern runs anyway. It appears while Postern holds the key
      itself and no marker file sits beside it.

      The notice tells you to acknowledge the copy in the Console. **No such control ships.** Nothing in
      the Console writes that marker, and no address answers it. So the notice still appears after you
      hold a good copy.

      Two things stop it. Take your copy of the key first, then choose one.

      1. **Write the marker yourself.** Postern looks for a file named `master.key.backed-up` beside the
         key. From the folder that holds `docker-compose.yml`:

         ```bash theme={"system"}
         docker compose exec app sh -c \
           'date -u +%Y-%m-%dT%H:%M:%SZ > /app/.pci/master.key.backed-up && chmod 600 /app/.pci/master.key.backed-up'
         ```

         Postern checks that the file is there. It never reads what is in it. Write the date anyway. It
         tells a later you when you took the copy.

      2. **Hold the key yourself.** Put `PCI_MASTER_KEY` in `.env` — a base64 32-byte key you keep —
         and restart. Make one with `openssl rand -base64 32`. Set it before the first start. A new key
         against accounts encrypted under an old one makes Postern refuse to start.

      A restore brings the notice back. The marker sat in the old storage area, and the restore makes a
      new one.
    </Note>

    Four commands cover the first week. Run them from the same folder:

    ```bash theme={"system"}
    docker compose ps                              # is it up
    docker compose logs -f app                     # read its log
    docker compose stop                            # pause it; docker compose start resumes
    docker compose pull && docker compose up -d    # move to a newer image
    ```

    To hold one release instead of the newest, add `POSTERN_VERSION=v0.1.0` to `.env`.
  </Step>

  <Step title="Open the Console" titleSize="h2" id="open-console">
    The Console answers at `http://localhost:8787`, and only on the machine Postern runs on.

    If Postern runs on the computer in front of you, open
    [http://localhost:8787](http://localhost:8787).

    If Postern runs on another computer, forward the Console port to your own computer. Run this **on
    your own computer**, not on the machine Postern runs on. Turn on SSH on that machine first.

    ```bash theme={"system"}
    ssh -N -L 127.0.0.1:8787:127.0.0.1:8787 YOUR_MACHINE
    ```

    Replace `YOUR_MACHINE` with however you normally reach that computer: a name, a `user@host`, or an
    IP address. The terminal then prints nothing and does not return to a prompt. That is the command at
    work, not a hang. Leave that window open, use a second terminal window for anything else, and press
    **Control-C** when you want to stop. Now open [http://localhost:8787](http://localhost:8787) on your
    own computer.

    Anyone who can reach the Console port can change your settings and create agent keys. It has no
    password, so it stays on this computer. Do not change the address in `docker-compose.yml` to your
    network. Do not put a tunnel service in front of it. The full argument is in
    [The Console has no authentication](/reference/ports#the-console-has-no-authentication).

    The Console opens on a page headed `Your gate is new.` with one button, **Add your first source**.
    The left sidebar lists **Gatehouse**, then the six sectors, then **Sources**, **Agents & keys**,
    **The ledger** and **Settings**. A sector is one area of your life: finance, mail, calendar,
    contacts, health, home. It is the smallest thing you can grant.

    <Frame caption="The Console on a fresh install. The one button reads Add your first source.">
      <img src="https://mintcdn.com/postern/E8R3wfogdGSukifu/images/gate-console-new.png?fit=max&auto=format&n=E8R3wfogdGSukifu&q=85&s=4c4a657f0b0bee2e54fd2271094d4613" alt="The Postern Console on a fresh install: a page headed “Your gate is new.”, with one button labelled “Add your first source”." width="1440" height="900" data-path="images/gate-console-new.png" />
    </Frame>

    To let an agent reach Postern from another device, see [Remote access](/start/remote-access). It is
    off by default, and it never moves the Console off this machine.
  </Step>
</Steps>

## Confirm it works

Run these on the machine Postern runs on.

```bash theme={"system"}
docker compose ps
curl -s localhost:8787/healthz
docker volume ls
```

* `docker compose ps` lists `db` and `app` as `running`, and `app` turns `healthy` about 20 seconds
  after that.
* `curl -s localhost:8787/healthz` prints `{"status":"ok"}`. The same address in a browser shows the
  same thing.
* `docker volume ls` lists two names that end in `pgdata` and `pci_master_key`.
* `.env` sits beside `docker-compose.yml` and holds one line that begins `POSTGRES_PASSWORD=`.

## If something went wrong

| What you see                                                                                                              | What to do                                                                                                                                                                          |
| ------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `docker compose up -d` stops at once and prints `set POSTGRES_PASSWORD in .env — generate one with: openssl rand -hex 24` | `.env` is missing or empty. Run the [step 2](#password) command again, in the folder that holds `docker-compose.yml`.                                                               |
| `app` starts and exits within seconds, and your password holds a character that is not a letter or a digit                | Run the [step 2](#password) command again, then `docker compose down -v && docker compose up -d`. That wipes the database, which is safe only before you connect anything.          |
| `db` is up, `app` cannot sign in to the database, and `.env` changed since the first start                                | Postgres kept the first password. Put the original line back in `.env`, then run `docker compose up -d`.                                                                            |
| `app` does not start, and another program on this computer already uses 8787 or 8788                                      | Move Postern's port. [Step 3](#start) carries the line to add.                                                                                                                      |
| The browser shows a not-found page while `curl -s localhost:8787/healthz` still answers `{"status":"ok"}`                 | Postern runs, and its Console files are not where it looked. Run `docker compose logs app`; the log names the folder it tried. Set `PCI_CONSOLE_DIR` in `.env` to the right folder. |
| The log prints `VAULT KEY IS NOT BACKED UP` at every start                                                                | That is the reminder, not a fault. [Step 3](#start) has the two ways to stop it.                                                                                                    |

## What you have now

Postern runs on a machine you own. Docker downloaded a finished image from
`ghcr.io/getpostern/postern` and built nothing on that machine. There is no Postern account and no
Postern server in the middle. You have connected nothing yet, and no agent can reach anything until
you grant it.

Three things are now yours to keep: the machine, on and updated; the one line in `.env`; and a copy
of the vault key.

## Next

<Columns cols={2}>
  <Card title="Mint your first key" href="/start/first-key">
    a name, the sectors it opens, a deadline · a few minutes · Postern shows the key once
  </Card>

  <Card title="Set up remote access" href="/start/remote-access">
    Tailscale, and Funnel only if a hosted agent needs a public address · about 20 minutes, once ·
    off by default
  </Card>
</Columns>
