---
title: Install your VOTPort instance
label: Installation
description: Run a licensed server release behind HTTPS with persistent state and file storage.
layout: docs
order: 141
---

## Before you start

Have Docker with the Compose plugin, a DNS name pointing to your server, a TLS reverse proxy, and the image reference supplied for your release. Keep the version and `sha256` digest together. The example below deliberately requires you to provide your release and credentials.

The service runs as UID and GID 1000. Create the mount directories on supported storage:

```sh
sudo install -d -o 1000 -g 1000 -m 0700 data
sudo install -d -o 1000 -g 1000 -m 0755 received outbound
```

The parent directories must also pass the [storage safety checks](/docs/self-hosting/storage/).

## Compose service

Create `compose.yaml`:

```yaml
services:
  votport:
    image: "${VOTPORT_IMAGE:?Set the released image reference and digest}"
    restart: unless-stopped
    stop_grace_period: 5m
    user: "1000:1000"
    ports:
      - "127.0.0.1:8321:8080"
    environment:
      VOTPORT_PUBLIC_URL: "${VOTPORT_PUBLIC_URL:?Set the HTTPS address}"
      VOTPORT_ADMIN_PASSWORD: "${VOTPORT_ADMIN_PASSWORD:?Set a strong password}"
      VOTPORT_TRUSTED_PROXIES: "${VOTPORT_TRUSTED_PROXIES:?Set the proxy peer range}"
    volumes:
      - ./data:/data
      - ./received:/received
      - ./outbound:/outbound
```

Supply those values using your deployment's secret and environment management. A released image reference has the form `ghcr.io/halideworks/votport:vX.Y.Z@sha256:<published-digest>`. Replace both placeholders with your approved release. The local administrator password must contain at least 12 characters. An Argon2 PHC value in `VOTPORT_ADMIN_PASSWORD_HASH` can replace the plain password variable.

`VOTPORT_PUBLIC_URL` must use HTTPS outside loopback development. The service refuses an invalid public URL at startup.

## HTTPS proxy

For Caddy running on the host:

```caddy
drop.example.com {
    reverse_proxy 127.0.0.1:8321
}
```

Replace the hostname, configure its DNS, and validate/reload Caddy using your host's service management. If Caddy runs in another container, use a private container network and its service address instead of that container's loopback address.

Set `VOTPORT_TRUSTED_PROXIES` to the address range from which the proxy actually connects. Docker's gateway address can change when a network is recreated. The `peer` field on an `admin_login_failed` log event identifies the socket peer; `ip` is the interpreted client address. Verify that requests from different clients remain distinguishable after proxy configuration.

## Start and verify

```sh
docker compose pull
docker compose up -d
docker compose logs --tail 100 votport
curl --fail http://127.0.0.1:8321/healthz
```

Open the HTTPS address and sign in. For a NAS receive root, first complete **Storage > Receiving storage** qualification using the [direct receiving guide](/docs/self-hosting/direct-receiving/). Then create a test request and receive a small file. Issue a delivery for it and download it. Check the receipt and confirm that the file appeared on the intended persistent volume.

**For native performance, also configure and forward the separate UDP ports for both send and receive.** Publishing HTTPS alone provides browser access, not native QUIC connectivity. Follow the [native networking guide](/docs/self-hosting/native-transfers/) and test from outside the facility.

Before onboarding users, configure [sign-in](/docs/administration/sign-in/), [backups](/docs/self-hosting/backups/), [monitoring](/docs/self-hosting/monitoring/), and the quotas and retention appropriate to your deployment.
