Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5.0 KiB
Quick Start
This guide walks through a first-time PIC installation on a clean Linux host.
Prerequisites
- Linux x86-64 host — Debian, Ubuntu, Fedora, RHEL, or Alpine
- 2 GB+ RAM, 10 GB+ disk
- Ports 53, 80, 443, 51820/udp, 25, 587, 993 available
The installer handles all software dependencies (git, docker, make, etc.) automatically.
Option A — One-line installer (recommended)
curl -fsSL http://install.pic.ngo | sudo bash
Always review the script before running it:
curl -fsSL http://install.pic.ngo | less
The installer:
- Detects your OS and installs Docker, git, make via the system package manager
- Creates a
picsystem user and adds it to thedockergroup - Clones the repository to
/opt/pic - Runs
make install(generates keys and config, writes a systemd unit) - Runs
make start-coreto bring up the core containers - Waits for the API to respond, then prints the wizard URL
When it finishes, open the URL it prints:
http://<host-ip>:8081/setup
Option B — Manual install
Use this if you want to control where PIC is installed, or if you are installing on a machine that already has Docker.
git clone http://git.pic.ngo/roof/pic.git pic
cd pic
sudo make install
make start-core
Then open http://<host-ip>:8081 in a browser.
Complete the setup wizard
The setup wizard appears automatically on first start. All API requests redirect to /setup until it is finished.
The wizard asks for:
- Cell name — used for hostnames and DDNS subdomain. Lowercase letters, digits, hyphens, 2–31 characters. Example:
myhome. - Domain mode — how HTTPS certificates are issued:
pic_ngo— automatic<cell-name>.pic.ngosubdomain with Let's Encrypt via DNS-01 (recommended for internet-facing cells)cloudflare— Let's Encrypt via Cloudflare DNS-01 (bring your own domain)duckdns— Let's Encrypt via DuckDNS DNS-01http01— Let's Encrypt via HTTP-01 (no wildcard; cell must be reachable on port 80)lan— internal CA, no internet required (for LAN-only installs)
- Timezone
- Services to enable — email, calendar, files, WireGuard
- Admin password — minimum 12 characters, must contain uppercase, lowercase, and a digit
Click Complete Setup. The wizard creates the admin account, writes cell identity to config/api/cell_config.json, and redirects to the login page.
Log in
After the wizard you are redirected to /login.
- Username:
admin - Password: the password you set in the wizard
Add a WireGuard peer
Go to Peers in the sidebar.
- Click Add Peer.
- Enter a peer name (e.g.
laptop). - The API generates a key pair and assigns the next available VPN IP automatically.
- Click the QR code icon to display the peer configuration as a QR code.
- Scan the QR code with a WireGuard client (Android, iOS, or the WireGuard desktop app).
Once connected, *.cell names resolve through the cell's CoreDNS and traffic can be routed through the cell.
Day-to-day operations
# Check container status and API health
make status
# Follow logs from all services
make logs
# Follow logs from a single service
make logs-api
make logs-wireguard
make logs-caddy
# Open a shell inside a container
make shell-api
make shell-dns
Backup and restore
make backup # archives config/ and data/ into backups/cell-backup-<timestamp>.tar.gz
make restore # list available backups
To restore manually:
tar -xzf backups/cell-backup-YYYYMMDD-HHMMSS.tar.gz
make start
Updating PIC
make update # git pull + rebuild + restart
Uninstalling
make uninstall # stops containers; prompts to also delete config/ and data/
Troubleshooting
Containers not starting
make logs
make logs-api
Look for errors about missing config files or port conflicts.
Port 53 already in use
On Ubuntu and Debian, systemd-resolved listens on port 53. Disable it:
sudo systemctl disable --now systemd-resolved
sudo rm /etc/resolv.conf
echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf
Then run make start again.
WireGuard container fails to load the kernel module
sudo modprobe wireguard
On minimal installs you may need wireguard-tools and the kernel headers for the running kernel.
API returns 428 and redirects to /setup
The first-run wizard has not been completed. Open http://<host-ip>:8081 and finish the wizard.
API returns 401 / UI shows "Not authenticated"
Your session expired or you have not logged in. Go to http://<host-ip>:8081/login.
API returns 503 "Authentication not configured"
The auth file exists but contains no accounts. To recover:
make reset-admin-password
This generates a new admin password and prints it.
Forgot the admin password
make show-admin-password # print current password
make reset-admin-password # generate a new random password