Status: Active | Owner: @roof | Applies to: main (2026-06) | Updated: 2026-06-11
Dev – Install Internals
This page describes what the installer does, the make targets, the systemd unit, and debugging options.
install.sh
The installer at https://install.pic.ngo is a Bash script (install.sh in the repository root). It is served from the DDNS VPS.
Flags
| Flag | Effect |
|---|---|
--debug |
Verbose output to terminal; stdout/stderr are tee'd to both the log file and the terminal |
--force |
Bypass the idempotency check (.installed file) |
PIC_DIR=/path |
Override install directory (default /opt/pic) |
PIC_DEBUG=1 |
Same as --debug, usable as an env var |
The install log is always written to /var/log/pic-install.log (or /tmp/pic-install.log if /var/log is not writable).
Seven steps
| Step | What happens |
|---|---|
| 1 | Detect OS from /etc/os-release; select apt, dnf, or apk |
| 2 | Install Docker, git, make; install and start host chrony |
| 3 | Create pic system user; add to docker group |
| 4 | Clone repository to ${PIC_DIR} (or git pull if already cloned) |
| 5 | Run make install — generates keys and config, writes systemd unit; scan stdout for the admin password banner and relay it to the terminal |
| 6 | Run make start-core to bring up the six core containers |
| 7 | Enable and start the pic systemd unit; poll http://127.0.0.1:3000/health for up to 60 seconds |
After step 7, the installer prints the browser URL for the setup wizard.
Idempotency
If ${PIC_DIR}/.installed exists and --force is not passed, the installer exits with a message to run make update instead.
make targets
All container operations go through make. The Makefile is at the project root.
Stack lifecycle
# Run on: the cell server host, from /opt/pic (or your repo root)
make start # docker compose up -d --build (full profile)
make start-core # bring up only the six core containers
make stop # docker compose down
make restart # docker compose restart
make status # container status + API /health check
make logs # follow all service logs
make logs-<svc> # e.g. make logs-api, make logs-wireguard
make shell-<svc> # shell inside container, e.g. make shell-api
Build
# Run on: your development machine or the cell server host
make build-api # rebuild cell-api image after code change
make build-webui # rebuild cell-webui image after code change
First install and maintenance
# Run on: the cell server host
make install # generate keys, config, systemd unit; write .installed
make update # git pull + rebuild + restart
make reinstall # full wipe of config/ and data/, then setup + start
make uninstall # stop containers; prompt to delete config/ and data/
Operations
# Run on: the cell server host, from /opt/pic
make backup # tar config/ + data/ into backups/cell-backup-<timestamp>.tar.gz
make restore # list available backup archives
make list-peers # show peers via API
make show-routes # wg show inside cell-wireguard
make show-admin-password # print current admin password
make reset-admin-password # generate and set a new random admin password
Tests
# Run on: your development machine
make test # pytest unit suite
make test-coverage # with coverage; htmlcov/
make test-api # API endpoint tests only
make test-integration # full integration tests (running stack required)
make test-integration-readonly # read-only integration tests
make test-e2e-deps # install Playwright (run once)
make test-e2e-api # API e2e tests
make test-e2e-ui # UI e2e tests
systemd unit
make install copies scripts/pic.service to /etc/systemd/system/pic.service and runs systemctl daemon-reload && systemctl enable pic. The unit starts and stops the PIC stack on boot and shutdown. The installer also calls systemctl start pic after make start-core succeeds.
make uninstall calls systemctl disable --now pic, removes the unit file, and runs systemctl daemon-reload.
On Alpine Linux (OpenRC), the installer skips systemd and uses OpenRC's rc-update instead.
Host NTP
The installer installs and enables chrony on the host (step 2). This is not the same as the cell-ntp container — it is the host's own time synchronisation, needed for:
- ACME certificate issuance (Let's Encrypt rejects challenges with skewed clocks)
pic.ngoDDNS registration (the registration endpoint requires a valid TOTP derived from the host clock)
If the installer cannot start chrony, it prints a warning. Proceed only after verifying the clock is accurate (date).
Debugging an install
To see the full verbose output of every installer step:
# Run on: your Linux server, as root
curl -fsSL https://install.pic.ngo | PIC_DEBUG=1 sudo -E bash
Or with the flag:
# Run on: your Linux server, as root
curl -fsSL https://install.pic.ngo | sudo bash -s -- --debug
The complete log (including all subprocess output) is always at /var/log/pic-install.log regardless of debug mode.
To debug a failing step after the installer exits:
# Run on: your Linux server
sudo cat /var/log/pic-install.log
.env overrides
A .env file in the project root overrides container IPs and port assignments. A .env file is not required — all variables have defaults. Create one only if you need to change ports or move containers to different IPs.
| Variable | Default | Description |
|---|---|---|
CELL_NETWORK |
172.20.0.0/16 |
Docker bridge subnet |
DNS_PORT |
53 |
DNS (UDP + TCP) |
NTP_PORT |
123 |
NTP (UDP) |
WG_PORT |
51820 |
WireGuard listen port (UDP) |
API_PORT |
3000 |
Flask API (127.0.0.1 only) |
WEBUI_PORT |
8081 |
Host port for the web UI |
FLASK_DEBUG |
(unset) | Set to 1 for Flask debug mode; do not use in production |
PUID / PGID |
current user | UID/GID for the WireGuard container |
Personal Internet Cell
New here?
Users
User – Connect to the VPN User – Use Your Services User – Troubleshooting
Admins
Admin – Overview Admin – Install and First Run Admin – Configure Domains and TLS Admin – Manage Services Admin – Configure Connectivity Admin – Manage Peers Admin – Back Up and Restore Admin – Logging and Audit Admin – Monitor and Troubleshoot
Developers
Dev – Overview Dev – Architecture Dev – Build a Store Service Dev – Service Manifest Reference Dev – API Reference Dev – Testing Dev – Install Internals
Decisions (ADRs)
ADR – 001 Store Images Are Signed and Verified by Cells ADR – 002 Named Connection Instances for Connectivity ADR – 003 All Optional Functionality Ships as Store Services
Meta
Meta – Glossary Meta – Template Runbook Meta – Template ADR
Archive
Archive – User Guide Archive – ADR 004 The Wiki Is the Single Documentation Source