Install on your own server

Install Workforce Intelligence on a Linux server you manage, using install.sh and an .env file you prepare.

For: Administrators deploying on their own hardware or any non-AWS host · Last updated

On a server without CloudFormation, nothing is automated for you. You copy two scripts to the box, prepare an .env file, and run install.sh.

Read Deployment requirements first.

1. Copy the scripts to the box#

Copy install.sh and find-setup-token.sh from the deploy/ directory of the Workforce Intelligence repository to a directory on the server, for example:

mkdir -p ~/wi && cd ~/wi

Use scp or paste them in. There is no compose file to transfer: install.sh extracts it from the wi-server image it pulls, so the file always matches that image's version. Make the scripts executable with chmod +x install.sh find-setup-token.sh.

2. Prepare .env#

install.sh looks for a file named .env next to itself. If it finds one, it uses it exactly as it is and never regenerates anything in it. You can create the file yourself, or let install.sh create it (below). The required values are the two secrets and the origin:

cd ~/wi
sudo tee .env >/dev/null <<ENVEOF
WI_MASTER_KEY=$(openssl rand -base64 32)
WI_SESSION_SECRET=$(openssl rand -base64 32)
WI_ALLOWED_ORIGIN=https://wi.example.com
WI_DOMAIN=wi.example.com
ENVEOF
sudo chmod 600 .env

Leave out WI_DOMAIN if you are not using a domain, and use http://<your-hostname-or-ip> for the origin. Browsers do not keep a sign-in made over plain HTTP, so use a domain with HTTPS for real use. Each key must appear once. You do not set WI_API_BASE_URL: the compose file derives it from WI_ALLOWED_ORIGIN. install.sh adds WI_DOKKU_SSH_HOST for you (see step 3). The meaning of every value is in Configuration reference.

Back up this file off the box straight away. WI_MASTER_KEY is generated once and cannot be regenerated. See Master key custody.

If you run install.sh with no .env present, it creates one for you. It generates WI_MASTER_KEY and WI_SESSION_SECRET and writes WI_DOMAIN and WI_ALLOWED_ORIGIN from the environment. In that case you must supply WI_ALLOWED_ORIGIN on the command line (the script stops with "WI_ALLOWED_ORIGIN must be set" otherwise).

3. Run install.sh#

cd ~/wi
GHCR_USER=<your-github-username> \
GHCR_TOKEN=<your-token> \
  ./install.sh

If you let the script create .env, add WI_ALLOWED_ORIGIN=https://<your-domain> (and WI_DOMAIN=<your-domain>) to the command line as well.

GHCR_USER and GHCR_TOKEN are your GitHub username and a classic personal access token with read:packages (see Deployment requirements). The script then:

  1. Makes sure .env has every required value. It adds WI_DOKKU_SSH_HOST, the hostname Builders use for git push, if it is missing: the value of the WI_DOKKU_SSH_HOST environment variable if you set one, otherwise WI_DOMAIN, otherwise the host part of WI_ALLOWED_ORIGIN. It never edits a line that is already there.
  2. Installs Docker through get.docker.com if Docker is not already installed.
  3. Runs sudo docker login ghcr.io. The login is stored in /root/.docker/config.json, which the in-app updater also uses.
  4. Pulls the wi-server image (WI_IMAGE_TAG, default latest) and extracts docker-compose.prod.yml from it into the script's directory.
  5. Pulls the other images and starts the stack with the Compose project name wi-prod: the database, the application, the web server, and Dokku.

The app container applies database migrations when it starts and never loads sample data, so you begin with an empty database and a company waiting to be claimed.

GHCR_USER and GHCR_TOKEN are needed only while the box is not logged in to ghcr.io. Docker remembers the login, so a re-run does not need them.

Upgrade the stack by re-running install.sh#

Run install.sh again to upgrade an installed box. It keeps your .env (the secrets are never regenerated), adds any newly required value, replaces docker-compose.prod.yml with the one inside the image it pulls, and recreates whatever changed, including new services and volumes. It also moves app and web to the pulled images without the backup that Settings → Updates takes first, so back up the database before you re-run it on a deployment that holds data. See What an update changes for when a release needs this.

Do not rename the wi-prod project. The scripts, the in-app updater, and the volume names all depend on it.

4. Open the app#

Open your address in a browser and continue with First-run setup token.

If the stack does not start#

  • A docker compose error about a missing variable means you are running Compose from a different directory than the one holding .env, or a required value is missing from it. Re-running install.sh adds the ones it can derive.
  • install.sh could not read the compose file out of the image. WI_IMAGE_TAG names an image that does not carry it. Use a newer tag.
  • docker login ghcr.io fails with "unauthorized" almost always means the token is wrong, expired, missing read:packages, or is a fine-grained token. Create a classic token.
  • The app container restarts repeatedly. Run sudo docker compose -p wi-prod logs app. The application refuses to start when its production checks fail, and the log line names the problem. See Production boot checks.
  • The sign-in page shows instead of the setup wizard. The database is not empty. Setup appears only when no company exists yet.