Install with Docker Compose
These steps are what e2e/self-host.sh runs in a throwaway compose project (with an image built from the checkout), so they are tested as written. The short version is on the quick start page.
You need a machine with Docker Engine 24+ and the Compose plugin (docker compose version), 2 GB of RAM and a few GB of disk. Crow HR runs as two containers, the app and PostgreSQL 17. There is no Redis and no queue.
1. Get the files
Section titled “1. Get the files”You need two files from the repository, docker-compose.yml and .env.example. The simplest way to get them is to clone it:
git clone https://github.com/Crow-Tech-Pte-Ltd/CrowHR.gitcd CrowHRCompose pulls the published image, ghcr.io/crow-tech-pte-ltd/crowhr:1, built for both amd64 and arm64 (Intel/AMD and 64-bit ARM servers). :1 follows the newest 1.x release. To run an image you build yourself, see build the image yourself.
2. Configure
Section titled “2. Configure”cp .env.example .envOpen .env and set one line, the address people will type in the browser:
PUBLIC_URL=https://hr.example.sg # behind a reverse proxy with HTTPS# orPUBLIC_URL=http://192.168.1.20:8080 # on your office network, no proxyEverything else has a working default. Recommended before you store real data:
BACKUP_PASSPHRASEencrypts backups. Without it, a backup file includes your encryption key in the clear. See backups and restore.TRUSTED_PROXIESis needed when you use a reverse proxy. See reverse proxy.
Every variable is listed in the configuration reference.
3. Start
Section titled “3. Start”docker compose up -ddocker compose ps # app and db should become "healthy" within a minute or twoOn first start Crow HR:
- creates a random database password in the
secretsvolume (the one-shotinitservice); - applies the database migrations;
- generates the encryption key into the data volume (
/data/secrets/crowhr.key, mode 600) and logs where it is; - prints a one-time setup code.
4. Finish setup in the browser
Section titled “4. Finish setup in the browser”docker compose logs app | grep -A3 'SETUP CODE'Open PUBLIC_URL. Every page redirects to /setup until setup is done. Enter the code, your company’s registered name, and the owner’s name, email and password. You are then signed in as the owner and offered two-step verification. Afterwards /setup no longer exists. The setup wizard page explains each field.
The code changes on every restart; docker compose exec app crowhr setup-code prints a fresh one. Five wrong codes from one address block it for 15 minutes.
5. Check it
Section titled “5. Check it”docker compose exec app crowhr doctordoctor checks the configuration, database, migrations, encryption key, disk space, clock and the proxy headers the app sees, and says what to fix. Owners see the same facts in Settings → System.
Then make your first backup and copy it somewhere else:
docker compose exec app crowhr backupWhat is where
Section titled “What is where”| Volume | Holds | Back up? |
|---|---|---|
data (/data) |
uploaded files, secrets/crowhr.key |
yes (backups include it) |
db |
PostgreSQL data | yes (backups include a pg_dump) |
backups (/backups) |
backup archives | copy them off this machine |
secrets (/run/crowhr-secrets) |
generated database password | no: a restore works with a fresh one |
Only the app’s port is published (HTTP_PORT, default 8080, on BIND_ADDRESS, default all interfaces). PostgreSQL has no host port. The app runs as uid 10001 on a read-only root filesystem.
Build the image yourself
Section titled “Build the image yourself”To run your own build of the checkout instead of the published image:
docker build -t crowhr:local .echo 'CROWHR_IMAGE=crowhr:local' >> .envdocker compose up -dThe build takes a few minutes. To upgrade, git pull, build again and run docker compose up -d; docker compose pull only updates published images.