What you need

  1. Install uv.

    uv is a fast Python package manager from Astral. localmail uses it to manage its environment and provide a globally installed CLI.

    macOS / Linux:

    curl -LsSf https://astral.sh/uv/install.sh | sh

    Windows (PowerShell):

    powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

    Open a new terminal so the updated PATH takes effect, then verify:

    uv --version
  2. Install the localmail CLI.

    uv tool install installs a Python package and exposes its console scripts on your PATH, in an isolated environment so it doesn't conflict with anything else.

    uv tool install \
      --python 3.12 \
      --from git+https://github.com/hherb/localmail \
      localmail

    To include the optional attachment-extraction dependencies (Docling, for higher-fidelity PDF parsing) install the extraction extra:

    uv tool install \
      --python 3.12 \
      --from git+https://github.com/hherb/localmail \
      --with 'localmail[extraction]' \
      localmail

    Verify the binary is on your PATH:

    localmail --version
    localmail --help
    Updating later

    Re-run the same uv tool install command with --reinstall, or simply uv tool upgrade localmail.

  3. Create the config file.

    localmail looks for ~/.config/localmail/config.toml by default. Override with $LOCALMAIL_CONFIG or localmail --config PATH ….

    mkdir -p ~/.config/localmail
    curl -fLo ~/.config/localmail/config.toml \
      https://raw.githubusercontent.com/hherb/localmail/main/config.example.toml

    Open the file in your editor and update the two required sections:

    [database]
    # Match the DSN you verified in Step 1. URL-encode special chars in
    # the password (e.g. '@' -> %40, ':' -> %3A).
    dsn = "postgresql://localmail:local%40%40mail@localhost:5532/localmail"
    
    [attachments]
    # Where attachment blobs land on disk. ~/localmail is fine. Pick a
    # drive with room for the attachments your mail accounts will pull in.
    root = "~/localmail"

    Leave the [daemon] block at its defaults for now. You will add [[accounts]] entries in Step 3, so don't worry about them yet.

  4. Apply the schema migrations.

    This is the first command that actually touches the database. It is idempotent — re-running it never harms anything.

    localmail init-db

    You should see one line per migration applied (0001_init.sql0017_messages_body_lang_pending_index.sql at the time of writing) and a final OK.

    If init-db fails

    The most common cause is a wrong DSN: typo, wrong port, password not URL-encoded. Test the DSN with psql first using the exact connection string from config.toml. The second most common cause is the pgvector extension not being installed (see Step 1).

  5. Confirm everything is wired up.

    localmail list-accounts

    You should get an empty list with no error. That's expected — you haven't added any accounts yet. Connecting to Postgres is the only thing this command tries to do, so a clean run here means the DSN, schema, and CLI all line up correctly.

What the install gave you

Path What lives there
~/.local/bin/localmail The CLI entry point. uv tool install placed it here.
~/.config/localmail/config.toml Your topology — DSN, attachments root, daemon settings, accounts.
~/localmail/blobs/ Empty for now. Attachments will land here, organised by SHA-256.
OS keyring (Keychain / Secret Service) Where passwords and OAuth refresh tokens will be stored — not in any file.
Postgres database localmail Schema is now in place. Empty tables until the first sync.

Alternative: install from a checkout

If you want to hack on localmail or track a specific branch, clone the repository and use uv sync instead:

git clone https://github.com/hherb/localmail
cd localmail
uv sync                       # installs deps into .venv
uv run localmail --help       # invoke through uv run
uv run localmail init-db

Every command in this manual still works — just prefix it with uv run.