This is for people who want to deploy Terrier in production.
Choose one of the following deployment methods:
- Docker Compose - Containerized deployment with all services included
- NixOS Module - Native deployment on NixOS with system-managed services
Copy the example and configure for your environment:
cp examples/.env.prod .env-
Update the
APP_BASE_URLandMINIO_PUBLIC_ENDPOINTvalues to match your deployment domain. -
Delete the "Unix sockets" section for the Redis and PostgreSQL configurations.
-
Change the
POSTGRES_PASSWORDandMINIO_ROOT_PASSWORDto secure password values:openssl rand -base64 24
-
Update the "OIDC Configuration" section to match your Identity Provider.
Configure your OIDC client with the following redirect URIs:
- Web:
{APP_BASE_URL}/auth/callback(e.g.,https://terrier.example.com/auth/callback) - Mobile:
terrier://auth/callback
- Web:
-
Add your Global Admins' Andrew emails to
ADMIN_EMAILS. -
Customize the host port mapping if necessary.
Copy the example:
cp examples/docker-compose.yml .Remove the ports on the postgres and valkey services unless you explicitly need to access them from outside the Docker network.
docker compose up -dThis command will start all the services defined in your docker-compose.yml file in detached mode. You can check the status of the services with:
docker compose psSee Reverse Proxy Configuration below.
Native deployment using the included NixOS module with system-managed services.
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
terrier.url = "github:ScottyLabs/terrier";
};
outputs = { self, nixpkgs, terrier, ... }: {
nixosConfigurations.your-server = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
terrier.nixosModules.default
./configuration.nix
];
};
};
}In your configuration.nix:
{ config, pkgs, ... }:
{
# PostgreSQL with Unix socket authentication
services.postgresql = {
enable = true;
package = pkgs.postgresql_17;
# Terrier module creates user and database
};
# Redis/Valkey with Unix socket
services.redis.servers.terrier = {
enable = true;
port = 0; # Unix socket only
user = "redis-terrier";
unixSocket = "/run/redis-terrier/redis.sock";
unixSocketPerm = 660;
};
# MinIO for object storage
services.minio = {
enable = true;
rootCredentialsFile = "/run/secrets/minio-credentials";
};
# Terrier application
services.terrier = {
enable = true;
environmentFile = "/run/secrets/terrier-env";
# Grant access to Redis socket
extraGroups = [ "redis-terrier" ];
# Wait for services to be ready
dependencies = [
"postgresql.service"
"redis-terrier.service"
"minio.service"
];
};
# Reverse proxy
services.nginx = {
enable = true;
virtualHosts."terrier.example.com" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://127.0.0.1:3000";
proxyWebsockets = true;
};
};
};
}Then create the secrets files modeled after examples/.env.prod. Remove the "TCP via Docker internal network" and "Optional host port mapping" sections, since those are meant for Docker Compose deployments.
Customize the passwords and other fields as described in the Docker Compose section. Use sops-nix or agenix to manage secrets securely.
nixos-rebuild switchTerrier supports both Unix socket and TCP connections for PostgreSQL and Redis.
| Method | DATABASE_URL Format |
Use Case |
|---|---|---|
| Unix Socket | postgres:///terrier?host=/run/postgresql |
NixOS, local deployment |
| TCP | postgres://user:pass@host:5432/terrier |
Docker, remote database |
Unix socket authentication is more secure (no passwords or network exposure) and has slightly lower latency, so it is preferred if you can use it.
| Method | REDIS_URL Format |
Use Case |
|---|---|---|
| Unix Socket | redis+unix:///path/to/redis.sock |
NixOS, local deployment |
| TCP | redis://host:6379 |
Docker, remote Redis |
| TCP with auth | redis://:password@host:6379 |
Authenticated Redis |
Terrier requires a reverse proxy for SSL termination. The application binds to 127.0.0.1:3000 by default. You can customize this using:
-
The
services.nginx.virtualHostsconfiguration in yourconfiguration.nix, if using NixOS. -
The
nginx.conffile + theTERRIER_PORTenvironment variable in.envotherwise.
You can use the example nginx.conf as a starting point.