Chapter 0524 min read
Install from binaries
Run CCHP with systemd on one Linux host: prepare PostgreSQL and Dragonfly, extract the binaries, write the configuration, and start the services, then set up a reverse proxy, PgDog, and upgrades.
This page shows how to run CCHP on one Linux host without containers: you extract the binaries from the release image, manage each service with systemd, and provide PostgreSQL and Dragonfly yourself. Every step requires root privileges on the host.
If you just want CCHP running as quickly as possible, Install with Docker Compose is simpler. To run each role as a separate workload on k3s, see Install on k3s / k8s. For all installation methods, see Installation overview.
All services on this page run on the same host. A multi-host deployment requires the same shared data directory mounted on every host and the host’s private network address set for each service, so we recommend installing on k3s / k8s instead.
Components and ports#
Every CCHP service runs from the same executable, installed as /usr/local/bin/cchp, and the --role flag selects the service. Binary installation uses the following systemd units:
The ports that each service listens on:
Warning: 8080, 8081, and the internal ports 8091, 8092, and 8093 listen on all interfaces. Restrict access with the host firewall: open only the reverse proxy’s ports to the internet, and never expose the internal ports.
What you need#
For the CPU, memory, and disk requirements of the whole machine, see Installation overview.
When it starts, each service adds up its own space caps on each file system and refuses to start if the total exceeds the file system’s capacity. At the default caps, the gateway’s node data takes about 14.9 GiB and its temporary directory about 8.3 GiB on a 4-core host, and the temporary directories of the control plane and the worker take 1 GiB each, for a total of about 25.2 GiB. That is why you reserve 26 GiB. Importing data from CC Hub needs extra space. See “CC Hub import components (optional)”.
Step 1: Create a system user and install FFmpeg#
All CCHP services run as the system user cchp:
useradd --system --home-dir /var/lib/cchp --shell /usr/sbin/nologin cchpInstall FFmpeg on the host that runs the gateway. On Debian and Ubuntu, use the system package repositories. On RHEL-based distributions, install ffmpeg from RPM Fusion:
apt install ffmpeg
ffprobe -versionResult: ffprobe -version prints version information.
Step 2: Prepare PostgreSQL and Dragonfly#
Install PostgreSQL#
Install PostgreSQL 18 and TimescaleDB by following the official PostgreSQL and TimescaleDB documentation. Then preload two libraries in postgresql.conf, and restart PostgreSQL for the change to take effect:
shared_preload_libraries = 'timescaledb,pg_stat_statements'If you did not install TimescaleDB, write only 'pg_stat_statements'. pg_stat_statements is used only for the Top statements view in System Management → Observability → Database, and CCHP runs without it.
Create the database and the owner role#
CCHP uses two database roles:
Create the owner role and the database as the PostgreSQL superuser. When the owner is a superuser, the migration completes the rest of the preparation automatically: it creates the extensions, creates cchp_runtime and grants it privileges, and grants the pg_read_all_settings membership:
OWNER_PASS="$(openssl rand -hex 32)"
sudo -u postgres psql -v ON_ERROR_STOP=1 -v owner_pass="$OWNER_PASS" <<'SQL'
CREATE ROLE hub LOGIN SUPERUSER PASSWORD :'owner_pass';
CREATE DATABASE app OWNER hub;
SQL
OWNER_URL="postgres://hub:${OWNER_PASS}@127.0.0.1:5432/app?sslmode=disable"Run steps 2 through 4 in the same root shell, because step 4 writes OWNER_URL into the configuration file. If PostgreSQL runs on another host, replace 127.0.0.1:5432 in the connection string with its address, and usually switch to sslmode=require.
Result: sudo -u postgres psql -Atc '\l app' shows the app database with hub as its owner.
If you can’t use a superuser#
If you can’t give the owner superuser privileges, for example on a managed database, the owner must have both BYPASSRLS and CREATEROLE, and must own the app database. Only a superuser can grant BYPASSRLS. A superuser must also create the extensions in the app database in advance:
CREATE ROLE hub LOGIN CREATEROLE BYPASSRLS PASSWORD '<owner password>';
CREATE DATABASE app OWNER hub;
\c app
CREATE EXTENSION IF NOT EXISTS timescaledb;
CREATE EXTENSION IF NOT EXISTS btree_gist;
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;In this case, the migration can’t grant pg_read_all_settings. After the first migration in step 5 creates cchp_runtime, run the following statement once as a superuser. If you don’t run it, CCHP runs as usual, and only Top statements is unavailable:
GRANT pg_read_all_settings TO cchp_runtime WITH ADMIN FALSE, INHERIT TRUE, SET FALSE;Warning: The grant must include WITH ADMIN FALSE, INHERIT TRUE, SET FALSE. If the runtime role holds the membership with other options, or has joined other roles, CCHP refuses to start, and the error message gives the REVOKE and GRANT statements to run.
Prepare Dragonfly#
Install and start Dragonfly by following the official Dragonfly documentation. In production mode, CCHP must connect to Dragonfly and refuses to start if the connection fails.
- The default connection URL is
redis://127.0.0.1:6379/0. If your address is different, changeDRAGONFLY_URLin step 4. - If you set a password, put it in the connection string, for example
redis://:<password>@127.0.0.1:6379/0. - Do not expose the Dragonfly port to the internet: listen only on a local address, or set a password and restrict access with a firewall.
Step 3: Extract the binaries#
The CCHP binary, the migration tool, Atlas, and the migration files are all in the release image ghcr.io/cch-hq/cch-plus. The following commands use Docker on the installation host to extract them:
docker pull ghcr.io/cch-hq/cch-plus:latest
id=$(docker create ghcr.io/cch-hq/cch-plus:latest)
install -d /usr/local/libexec/cchp /usr/local/share/cchp
docker cp "$id":/usr/local/bin/gateway /usr/local/bin/cchp
docker cp "$id":/usr/local/bin/migrate /usr/local/libexec/cchp/migrate
docker cp "$id":/usr/local/bin/atlas /usr/local/libexec/cchp/atlas
docker cp "$id":/migrations /usr/local/share/cchp/migrations
docker rm "$id"latest is the latest stable release. To install a specific version, replace both occurrences of latest with the version number, for example 1.0.11.
If the installation host has no Docker, run the same copy commands on another machine that has Docker, and then copy the four items to the same paths on the installation host. If the two machines have different architectures, add --platform linux/amd64 or --platform linux/arm64 to both docker pull and docker create.
Result: /usr/local/bin/cchp help prints the list of subcommands.
Step 4: Write the configuration files#
All services share the environment file /etc/cchp/cchp.env. The owner credentials that the migration uses are kept in a separate file, /etc/cchp/cchp-migrate.env, which only cchp-migrate.service reads.
First, create a working directory to keep all of the following files in:
install -d -m 0700 /root/cchp-install
cd /root/cchp-installCreate the following two files in the working directory:
cchp.env
APP_ENV=production
# Migration tool and migration files installed in step 3
ATLAS_BIN=/usr/local/libexec/cchp/atlas
CCH_MIGRATE_BIN=/usr/local/libexec/cchp/migrate
CCH_MIGRATIONS_DIR=/usr/local/share/cchp/migrations
# DATABASE_URL is written by the install commands below
DATABASE_URL=
CCHP_DATABASE_RUNTIME_ROLE=cchp_runtime
DRAGONFLY_URL=redis://127.0.0.1:6379/0
# Secrets, written by the install commands below
CCH_TOKEN_PEPPER=
CCH_MASTER_KEY=
DRAGONFLY_KEY_SECRET=
# PostgreSQL data directory, used to read the database's disk capacity
CCHP_DATABASE_STORAGE_SOURCE=filesystem
CCHP_DATABASE_STORAGE_PATH=/var/lib/postgresql/18/main
# Public URL. Change it to the HTTPS URL after you set up HTTPS
CCH_PUBLIC_BASE_URL=http://localhost:8080
# Same as CCH_PUBLIC_BASE_URL
CCH_CONTROL_PLANE_ALLOWED_ORIGINS=http://localhost:8080
CCH_GATEWAY_PUBLIC_URL=http://localhost:8081
# Audit logs are stored only in PostgreSQL
CCH_SIEM_MODE=disabled
# For LAN testing only; see "Reduced-security deployment (LAN testing)"
CCH_INSECURE_DEPLOYMENT=falsecchp-migrate.env
# Written by the install commands below
ATLAS_DATABASE_URL=
CCHP_DATABASE_RUNTIME_PASSWORD=CCHP_DATABASE_STORAGE_PATH is the PostgreSQL data directory. /var/lib/postgresql/18/main is the default on Debian and Ubuntu. On other systems, look it up with sudo -u postgres psql -Atc 'SHOW data_directory' and replace the value. For what each variable means, see “Configuration reference”.
Then install the two files, and write the database connection strings and the secrets:
: "${OWNER_URL:?OWNER_URL is not set, see step 2}"
install -d -m 0750 -g cchp /etc/cchp
install -m 0640 -g cchp cchp.env /etc/cchp/cchp.env
install -m 0600 cchp-migrate.env /etc/cchp/cchp-migrate.env
sed -i -E '/^(DATABASE_URL|CCH_TOKEN_PEPPER|CCH_MASTER_KEY|DRAGONFLY_KEY_SECRET)=/d' /etc/cchp/cchp.env
sed -i -E '/^(ATLAS_DATABASE_URL|CCHP_DATABASE_RUNTIME_PASSWORD)=/d' /etc/cchp/cchp-migrate.env
RTPASS="$(openssl rand -hex 32)"
printf 'DATABASE_URL=postgres://cchp_runtime:%s@127.0.0.1:5432/app?sslmode=disable\n' "$RTPASS" >> /etc/cchp/cchp.env
printf '%s=%s\n' ATLAS_DATABASE_URL "$OWNER_URL" CCHP_DATABASE_RUNTIME_PASSWORD "$RTPASS" >> /etc/cchp/cchp-migrate.env
for key in CCH_TOKEN_PEPPER CCH_MASTER_KEY DRAGONFLY_KEY_SECRET; do
printf '%s=%s\n' "$key" "$(openssl rand -hex 32)" >> /etc/cchp/cchp.env
doneThe two sed commands delete the empty placeholder lines in the two files, and the commands after them write the generated values. If PostgreSQL is not on this host, also change the address and sslmode in DATABASE_URL.
Warning: Run the commands above only once, at the first installation. CCH_MASTER_KEY decrypts the saved provider keys. If you generate a new key after the first start, the saved provider keys can no longer be decrypted. Back up the two files under /etc/cchp/ to a safe location.
Check the names of the variables that have values (the values are not shown):
grep -oE '^(DATABASE_URL|CCH_TOKEN_PEPPER|CCH_MASTER_KEY|DRAGONFLY_KEY_SECRET)=.' /etc/cchp/cchp.env | cut -d= -f1
grep -oE '^(ATLAS_DATABASE_URL|CCHP_DATABASE_RUNTIME_PASSWORD)=.' /etc/cchp/cchp-migrate.env | cut -d= -f1Result: The first command prints DATABASE_URL and the names of the three secrets, one per line. The second prints ATLAS_DATABASE_URL and CCHP_DATABASE_RUNTIME_PASSWORD.
Step 5: Install the systemd units and start the services#
Save the following four unit files in the working directory:
cchp-migrate.service
[Unit]
Description=CCHP database migration (schema, data reconciliation, runtime role grants, row level security)
After=network-online.target postgresql.service cchp-pgdog.service
Wants=network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
User=cchp
Group=cchp
EnvironmentFile=/etc/cchp/cchp.env
EnvironmentFile=/etc/cchp/cchp-migrate.env
Environment=CCH_SCRATCH_DIR=/var/cache/cchp/migrate
CacheDirectory=cchp/migrate
CacheDirectoryMode=0700
ExecStart=/usr/local/bin/cchp db migrate
TimeoutStartSec=744min
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=truecchp-control.service
[Unit]
Description=CCHP control plane (dashboard + API)
After=network-online.target cchp-migrate.service
Wants=network-online.target
Requires=cchp-migrate.service
[Service]
User=cchp
Group=cchp
EnvironmentFile=/etc/cchp/cchp.env
StateDirectory=cchp/data
StateDirectoryMode=0700
Environment=CCH_SCRATCH_DIR=/var/cache/cchp/control
CacheDirectory=cchp/control
CacheDirectoryMode=0700
# Internal port 8092. Do not expose it
Environment=CCH_NODE_INTERNAL_URL=http://127.0.0.1:8092
ExecStart=/usr/local/bin/cchp api --role=control --addr=:8080
Restart=on-failure
RestartSec=5
TimeoutStopSec=45
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
[Install]
WantedBy=multi-user.targetcchp-gateway.service
[Unit]
Description=CCHP gateway (LLM data plane)
After=network-online.target cchp-migrate.service
Wants=network-online.target
Requires=cchp-migrate.service
[Service]
User=cchp
Group=cchp
EnvironmentFile=/etc/cchp/cchp.env
# /var/lib/cchp/node must be on a local disk
StateDirectory=cchp/data cchp/node
StateDirectoryMode=0700
Environment=CCH_SCRATCH_DIR=/var/cache/cchp/gateway
CacheDirectory=cchp/gateway
CacheDirectoryMode=0700
# Internal port 8091. Do not expose it
Environment=CCH_ARCHIVE_INTERNAL_URL=http://127.0.0.1:8091
Environment=CCH_REPLAY_OWNER_PROXY_NODES=%H=http://127.0.0.1:8081
Environment=CCH_ALLOW_EMPTY_ROUTING_BOOTSTRAP=true
ExecStart=/usr/local/bin/cchp api --role=gateway --addr=:8081
Restart=on-failure
RestartSec=5
TimeoutStopSec=930
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
[Install]
WantedBy=multi-user.targetcchp-worker.service
[Unit]
Description=CCHP async worker
After=network-online.target cchp-migrate.service
Wants=network-online.target
Requires=cchp-migrate.service
[Service]
User=cchp
Group=cchp
EnvironmentFile=/etc/cchp/cchp.env
# The first entry is for the optional CC Hub import components
Environment=PATH=/usr/local/libexec/cchp/dump/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
StateDirectory=cchp/data
StateDirectoryMode=0700
Environment=CCH_SCRATCH_DIR=/var/cache/cchp/worker
CacheDirectory=cchp/worker
CacheDirectoryMode=0700
# Internal port 8093. Do not expose it
Environment=CCH_NODE_INTERNAL_URL=http://127.0.0.1:8093
ExecStart=/usr/local/bin/cchp api --role=worker
Restart=on-failure
RestartSec=5
TimeoutStopSec=45
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
[Install]
WantedBy=multi-user.targetInstall the units, and enable and start the three services:
install -m 0644 cchp-migrate.service cchp-control.service cchp-gateway.service cchp-worker.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now cchp-control cchp-gateway cchp-workerThe dependencies in the units determine the start order:
- All three services declare
Requires=andAfter=cchp-migrate.service, so systemd runs the migration first.systemctlreturns only after the migration finishes. - After the migration succeeds,
cchp-migrate.servicestays in theactive (exited)state, and the three services start next. If the migration fails, none of the three services start. cchp-migrate.servicehas no[Install]section and does not need to be enabled separately. At every boot, it runs first, along with the three services.- The migration can run for up to 744 minutes (
TimeoutStartSec), which covers the time budget of all migration phases.
Result: The command returns without errors.
Step 6: Check that the services are ready#
systemctl status cchp-migrate cchp-control cchp-gateway cchp-worker --no-pager
journalctl -u cchp-migrate --no-pager | grep 'database observability'
curl -fsS http://127.0.0.1:8080/readyz
curl -fsS http://127.0.0.1:8081/readyz
curl -fsS http://127.0.0.1:8082/readyzResult: cchp-migrate shows active (exited), and the other three show active (running). All three /readyz requests succeed.
The database observability line in the migration log reports the two prerequisites for Top statements, for example database observability: pg_read_all_settings membership=granted, pg_stat_statements extension=created. What each value means:
Step 7: Complete the setup wizard#
-
Get the one-time setup token from the control plane log:
bash journalctl -u cchp-control | grep 'CCHP setup token'If you can’t find the token in the log, see “The setup token is not in the log” under “Frequently asked questions”.
-
Open
http://localhost:8080. You can sign in only on an HTTPS URL or a loopback address, so if you connect from another computer, set up SSH port forwarding first, and then openhttp://localhost:8080in your local browser:bash ssh -L 8080:127.0.0.1:8080 -L 8081:127.0.0.1:8081 root@cchp.example.comTo open the console directly by its domain, complete “Set up a reverse proxy and HTTPS” first.
-
On the Enter the setup token page, paste the token into Setup token, and click Verify and continue.
-
Complete the seven steps in order: Site, Administrator, Two-step, Email, Sign-in, Database, and Organization. You can skip Two-step and Email. For how to fill in each step, see Step 6: Enter the setup token and Step 7: Complete the setup wizard in Install with Docker Compose. The steps are the same for all three installation methods.
-
In the Database step (the page title is Check database observability): if the owner is a superuser and
pg_stat_statementsis preloaded, the check passes right away. Otherwise, the page shows the reason and the SQL that a superuser needs to run. You can also click Skip, handle it later in System Management. -
In the Organization step, fill in Organization name and Team name, choose Prepaid or Postpaid under Organization type, and click Create and continue. Creating the first organization completes the installation.
Result: The browser opens the console. There are no providers yet, so the gateway returns a no-route error for proxied requests. Later sections show how to create providers and LLM keys.
Configuration reference#
cchp.env#
cchp-migrate.env#
Variables in the unit files#
The following variables are set in each unit’s Environment= lines, because each service has its own values. Do not put them in cchp.env: values in cchp.env override Environment= in the units. To change them, add an override with systemctl edit <unit-name>.
Files and directories#
Set up a reverse proxy and HTTPS#
To open the console from another computer, you must use HTTPS. Put the console and the gateway behind TLS reverse proxies on two separate domains, for example cchp.example.com for the console and api.cchp.example.com for the gateway.
In binary installation, the control plane listens on local port 8080 and the gateway on 8081, the same as in a Docker Compose deployment, so the reverse proxy configuration is also the same: set up Caddy or Nginx by following Step 5: Set up a reverse proxy and HTTPS in Install with Docker Compose.
Then change the three public URLs to HTTPS URLs, and restart the three services:
CCH_PUBLIC_BASE_URL=https://cchp.example.com
CCH_CONTROL_PLANE_ALLOWED_ORIGINS=https://cchp.example.com
CCH_GATEWAY_PUBLIC_URL=https://api.cchp.example.comsystemctl restart cchp-control cchp-gateway cchp-workerResult: Opening https://cchp.example.com in a browser shows the sign-in page, and the gateway URL that users see in the console is https://api.cchp.example.com.
Behind a reverse proxy, the client IP in request logs and audit records is the reverse proxy’s address by default. To identify the real source IP, see Every client IP in request logs is the same address in the Install with Docker Compose FAQ.
Use the PgDog connection pool (optional)#
PgDog is a connection pool (in transaction mode) that runs between CCHP and PostgreSQL. Each PostgreSQL backend connection caches the partitions and indexes it has accessed, so the more connections there are, the more memory they use. PgDog pools the connections of the three services, with at most 24 server connections to PostgreSQL. A single-host installation also works fine without PgDog.
Run all of the following commands in the working directory /root/cchp-install.
-
Download the PgDog v0.1.60 Linux binary, install it as
/usr/local/bin/pgdog, and create a system user to run it:bash pkg="pgdog-v0.1.60-$(uname -m)-unknown-linux-gnu" curl -fsSLO "https://github.com/pgdogdev/pgdog/releases/download/v0.1.60/${pkg}.tar.gz" tar xzf "${pkg}.tar.gz" install -m 0755 "${pkg}/pgdog" /usr/local/bin/pgdog useradd --system --shell /usr/sbin/nologin pgdog install -d /etc/pgdog -
Save the PgDog configuration as
/etc/pgdog/pgdog.toml. It listens only on a local address:/etc/pgdog/pgdog.tomltoml [general] host = "127.0.0.1" # Number of server connections to PostgreSQL default_pool_size = 24 min_pool_size = 2 prepared_statements_limit = 4096 # Replace server connections about every 5 minutes to free PostgreSQL's caches server_lifetime = 300000 server_lifetime_jitter = 60000 [[databases]] name = "app" host = "127.0.0.1"default_pool_sizemust be smaller than PostgreSQL’smax_connectionsminus the direct sessions (LISTEN, scheduled-task locks, and migrations) and the connections of other clients. -
Save the PgDog users file as
/etc/pgdog/users.toml. LeaveCHANGE_MEas it is. The next command replaces it with the runtime role’s password:/etc/pgdog/users.tomltoml # Must match the user, database name, and password in DATABASE_URL [[users]] name = "cchp_runtime" database = "app" password = "CHANGE_ME"bash chown root:pgdog /etc/pgdog/users.toml chmod 0640 /etc/pgdog/users.toml RTPASS="$(sed -n 's/^CCHP_DATABASE_RUNTIME_PASSWORD=//p' /etc/cchp/cchp-migrate.env)" sed -i "s/CHANGE_ME/${RTPASS}/" /etc/pgdog/users.toml -
Save the unit file as
/etc/systemd/system/cchp-pgdog.service, and then enable and start it:/etc/systemd/system/cchp-pgdog.serviceini [Unit] Description=PgDog connection pooler for CCHP After=network-online.target postgresql.service Wants=network-online.target Before=cchp-control.service cchp-gateway.service cchp-worker.service [Service] User=pgdog Group=pgdog ExecStart=/usr/local/bin/pgdog --config /etc/pgdog/pgdog.toml --users /etc/pgdog/users.toml Restart=on-failure RestartSec=2 # After SIGINT, PgDog waits up to 60 seconds for in-flight transactions to finish KillSignal=SIGINT TimeoutStopSec=65 NoNewPrivileges=true ProtectSystem=strict ProtectHome=true PrivateTmp=true [Install] WantedBy=multi-user.targetbash systemctl daemon-reload systemctl enable --now cchp-pgdogThe units of the three services do not declare a dependency on
cchp-pgdog.service, so you must enable it separately. -
Save the original direct connection URL as
DATABASE_DIRECT_URL, changeDATABASE_URLto PgDog’s port6432, and then restart the three services.DATABASE_DIRECT_URLis used forLISTENand for the session locks of scheduled tasks, which can’t go through a transaction-mode connection pool:bash grep '^DATABASE_URL=' /etc/cchp/cchp.env | sed 's/^DATABASE_URL=/DATABASE_DIRECT_URL=/' >> /etc/cchp/cchp.env sed -i '/^DATABASE_URL=/s|@127.0.0.1:5432/|@127.0.0.1:6432/|' /etc/cchp/cchp.env systemctl restart cchp-control cchp-gateway cchp-worker
The migration always connects directly to PostgreSQL with ATLAS_DATABASE_URL from cchp-migrate.env, and never goes through PgDog.
Result: systemctl status cchp-pgdog shows active (running), and /readyz succeeds for all three services.
CC Hub import components (optional)#
To import CC Hub data in the console, the worker needs the dump decoder. If you don’t use CC Hub import, skip this section.
The decoding script is built into the CCHP binary, so you only need to install the Python environment that it uses. Before you install, make sure that:
python3is version 3.11 or later (check withpython3 --version) and has thevenvmodule. On Debian and Ubuntu, install the module withapt install python3-venv.- The host can reach PyPI.
Install the environment to /usr/local/libexec/cchp/dump, and check that the dependencies can be imported:
python3 -m venv /usr/local/libexec/cchp/dump
/usr/local/libexec/cchp/dump/bin/python3 -m pip install \
pendulum==3.2.0 pgdumplib==4.0.0 pglast==8.5 python-dateutil==2.9.0.post0 \
six==1.17.0 toposort==1.10 tzdata==2026.5
ln -sfn python3 /usr/local/libexec/cchp/dump/bin/cchub-dump-python
/usr/local/libexec/cchp/dump/bin/cchub-dump-python -c 'import pgdumplib, pglast'Result: The last command prints nothing and reports no errors.
cchp-worker.service already adds /usr/local/libexec/cchp/dump/bin to PATH, and the worker finds cchub-dump-python there, so you don’t need to restart the worker after the installation. This directory is a Python virtual environment and can’t be moved to another location. After you upgrade the host’s Python, delete this directory and run the commands above again.
Importing also needs enough disk space:
- When the control plane receives an upload, the file system that holds
/var/cache/cchp/controlneeds as much free space as the size of the uploaded file. - When the worker processes an export file of size S with N usage records, the file system that holds
/var/cache/cchp/workerneeds about S + N × 2.5 KiB of free space. For example, a 50 GB pg_dump with 96 million rows needs about 300 GB.
Backup#
Export the database:
sudo -u postgres pg_dump -Fc -d app > "/root/cchp-$(date +%F).dump"Result: A backup file named cchp- plus the date is created under /root.
Before you back up the node data, stop the gateway so that you don’t copy half-written files:
systemctl stop cchp-gateway
tar czf "/root/cchp-node-$(date +%F).tar.gz" -C /var/lib/cchp/node .
systemctl start cchp-gatewayFor what to watch for when you restore the database, see Backup and restore in Install with Docker Compose.
Upgrade#
Migrations run only forward and can’t be rolled back. Before you upgrade, back up the database and the two files under /etc/cchp/ as described in “Backup”.
docker pull ghcr.io/cch-hq/cch-plus:latest
id=$(docker create ghcr.io/cch-hq/cch-plus:latest)
sudo -u postgres pg_dump -Fc -d app > "/root/cchp-$(date +%F).dump"
systemctl stop cchp-control cchp-gateway cchp-worker cchp-migrate
rm -rf /usr/local/share/cchp/migrations
docker cp "$id":/usr/local/bin/gateway /usr/local/bin/cchp
docker cp "$id":/usr/local/bin/migrate /usr/local/libexec/cchp/migrate
docker cp "$id":/usr/local/bin/atlas /usr/local/libexec/cchp/atlas
docker cp "$id":/migrations /usr/local/share/cchp/migrations
docker rm "$id"
systemctl start cchp-control cchp-gateway cchp-workerdocker pull gets the latest stable release. To upgrade to a specific version, replace both occurrences of latest with the version number, for example 1.0.11. What each command does:
- Pull the new image and create the container first, to shorten the downtime. If you run only
docker create, it uses the oldlatestimage that is already on the host. - Stop
cchp-migratealong with the services, so that the migration runs again at the next start. Stopping the gateway waits for in-flight requests to finish, for up to 930 seconds (about 15 minutes). - Delete the old directory before you copy the migration files. If the target directory already exists,
docker cpcopies the new directory into the old one instead of replacing it. - When you start the three services, systemd runs the migration first. When you upgrade across several versions, one migration applies all the missing versions in order. If the migration is slow,
systemctl startkeeps waiting. You can watch the progress in another terminal withjournalctl -fu cchp-migrate.
If the unit files or cchp.env change in the new version, update these files according to this page, and run systemctl daemon-reload before you start the services. If you installed the CC Hub import components, delete /usr/local/libexec/cchp/dump and run their installation commands on this page again.
Result: Current version on System Management → About shows the new version number, and /readyz succeeds for all three services.
To go back to an older version, first restore the database from the backup you made before the upgrade. Then replace latest in the commands above with the older version number, for example 1.0.11, swap the four items back with the same steps, and start the services. Migrations run only forward, so swapping the files back alone can’t undo the changes that the new version made to the database.
View logs#
Each service writes its logs to standard output and standard error, one JSON object per line, and journald collects them:
journalctl -u cchp-migrate -b
journalctl -u cchp-control -f
journalctl -u cchp-gateway --since "1 hour ago"
journalctl -u cchp-worker -f
journalctl -u cchp-pgdog -fFrequently asked questions#
A service fails to start, and the log says the public URL is invalid#
If CCH_PUBLIC_BASE_URL is neither an HTTPS URL nor a loopback address (for example http://203.0.113.10:8080), the error in the log contains invitation public base URL is invalid. If the URL contains a path, the error is public origin must not contain a path. Change it to http://localhost:8080 and connect through SSH port forwarding, or set up the reverse proxy first and change it to an HTTPS URL without a path. Then run systemctl restart cchp-control cchp-gateway cchp-worker.
The gateway fails to start, and the log shows ffprobe is not on PATH#
FFmpeg is not installed on the host. Install it as described in step 1, and then run systemctl restart cchp-gateway.
The migration fails, and the log shows must be SUPERUSER or BYPASSRLS#
The owner role in ATLAS_DATABASE_URL is not a superuser and does not have BYPASSRLS. Run ALTER ROLE hub BYPASSRLS; as a superuser, and then run systemctl start cchp-control cchp-gateway cchp-worker to run the migration again and start the services.
The services refuse to start with holds role memberships that cchp does not accept#
The runtime role cchp_runtime has joined other roles, or the grant options of pg_read_all_settings are wrong, for example because GRANT pg_read_all_settings TO cchp_runtime; was run without options. Run the REVOKE statements from the error message as a superuser, and then grant the membership again as written in step 2.
After an upgrade, /readyz returns 503 and lists missing_migrations#
The new version’s migrations have not run yet. A common cause is restarting only the three services: cchp-migrate is still in the active (exited) state, and restarting the services does not run it again. Run systemctl restart cchp-migrate. The three services declare Requires=cchp-migrate.service, so they restart along with it and start after the migration finishes.
None of the three services start after a reboot#
cchp-migrate.service is ordered only after postgresql.service, and it does not retry automatically after a failure. If PostgreSQL runs some other way or on another host, the database may not be ready yet at boot, and when the migration fails, none of the three services start. Check the cause with journalctl -u cchp-migrate -b, and after the database is ready, run systemctl start cchp-control cchp-gateway cchp-worker.
Startup fails, and the log shows storage_volume_too_small#
The sum of the space caps of the subdirectories exceeds the file system’s capacity. The error message lists the volume, the capacity, each cap, and the shortfall. Enlarge the file systems that hold /var/lib and /var/cache, and then restart the affected services.
The setup wizard shows “You opened a different address”#
The address open in your browser is different from CCH_PUBLIC_BASE_URL. Open exactly the same address as CCH_PUBLIC_BASE_URL, or change CCH_PUBLIC_BASE_URL and run systemctl restart cchp-control.
The setup token is not in the log#
Print the current token as the service user, with the service’s environment variables:
sudo -u cchp sh -c 'set -a; . /etc/cchp/cchp.env; exec cchp identity setup-token'If the token has already been used but the installation is not complete, this command generates a new token. Running systemctl restart cchp-control also prints a new token in the log. If an administrator has already been created, the command fails with installation already has users. In that case, sign in directly.
Related pages#
- Installation overview
- Install with Docker Compose: the reverse proxy configuration and each step of the setup wizard
- Install on k3s / k8s