Server Installation
This content is for the 1.0.0 version. Switch to the latest version for up-to-date documentation.
This page covers installing PocketCmd Server, the first run that pairs your first device, the background service it installs and how to upgrade or remove it. The server is a single binary, pcmd. Pre-built binaries exist for Linux and macOS on amd64 and arm64.
Install and run it as the regular user who should own the server, not as root: the service and its data in ~/.pocketcmd belong to that user.
Install script
Section titled “Install script”curl -fsSL https://pocketcmd.com/install.sh | sh -s -- --api 1--api takes the API version of your app. The app shows this command with its own version under Teams › Set up a new server and in Add Server › Import from Computer, so copying it from there always gets you a matching server. The server’s major version is its API version, so --api 1 installs the newest stable 1.x.y release. Without --api the script installs the latest release, which may belong to a newer API than your app speaks. See Versions and compatibility.
The script:
- Detects the OS (
LinuxorDarwin) and architecture (x86_64/amd64orarm64/aarch64) and refuses anything else. In a Rosetta shell on an Apple silicon Mac it installs the native arm64 build. - Picks the release: the newest
vN.x.yfor--api N, otherwise the latest release ofpocketcmd/pocketcmd-server. - Downloads
pocketcmd_<os>_<arch>.tar.gzandchecksums.txtfrom that GitHub release. - Verifies the archive’s SHA-256 checksum and aborts on a mismatch.
- If
cosignis installed, verifies the keyless signature onchecksums.txtand aborts if it fails. Withoutcosignthis step is skipped. - Installs
pcmdto/usr/local/bin(usingsudoif the directory is not writable) or, failing that, to~/.local/bin, and warns if that directory is not on yourPATH.
The script reads these environment variables. Set them on the sh side of the pipe:
| Variable | Effect |
|---|---|
PCMD_API |
Same as --api. |
PCMD_VERSION |
Release tag to install, for example v1.0.0. Overrides --api. Default: the latest release. |
PCMD_INSTALL_DIR |
Target directory for the binary. |
PCMD_BASE_URL |
A mirror directory containing the archives and checksums.txt. Bypasses GitHub. |
PCMD_REPO |
owner/name to install from. Default: pocketcmd/pocketcmd-server. |
PCMD_COSIGN_IDENTITY |
Certificate identity regex for the cosign check. |
curl -fsSL https://pocketcmd.com/install.sh | PCMD_VERSION=v1.0.0 shOther ways to install
Section titled “Other ways to install”Release archive. Download the archive for your platform from the releases page, check it against checksums.txt and put the pcmd binary from it on your PATH. Pick a release whose major version matches your app’s API version. To verify a release by hand:
cosign verify-blob \ --certificate checksums.txt.pem \ --signature checksums.txt.sig \ --certificate-identity-regexp 'https://github.com/pocketcmd/pocketcmd-server/.*' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ checksums.txt
gh attestation verify pocketcmd_linux_amd64.tar.gz --repo pocketcmd/pocketcmd-serverGo. Requires Go 1.26 or later. A go install build reports its version as 1.0.0-dev; only release builds carry the real version, commit and build date.
go install github.com/pocketcmd/pocketcmd-server/cmd/pcmd@latestFrom source.
git clone https://github.com/pocketcmd/pocketcmd-server.gitcd pocketcmd-servermake build # ./pcmd for the current platformmake test # go test ./...make all # pcmd-{darwin,linux}-{amd64,arm64}The SQLite driver is pure Go, so the binary builds with CGO_ENABLED=0 and has no runtime dependencies.
First run
Section titled “First run”Run the binary with no arguments:
pcmdOn systemd and macOS it refuses to run as root or under sudo. Log in as the user who should own the server instead.
On the first run it:
- Asks which network interface the server should listen on, but only when the machine has more than one address and you did not pass
--host. It offers the IPv4 addresses first, then global IPv6 addresses. There are no other prompts. - Creates the server’s TLS identity certificate in
~/.pocketcmd/tls/. - Starts the HTTPS server on port
8767(or--port) and prints a QR code plus the samepocketcmd://URL afterOr copy:. When more than one address exists, it lists them all. - Waits up to five minutes for a device to pair (
--pair-timeout;0waits forever). Each code is valid for 60 seconds and works once; when one expires, the command printsThe pairing code expired. Scan the new code:and a new QR code. - After pairing, writes
~/.pocketcmd/config.jsonwith the port, bind address and advertised address, so later commands reuse them. Then it starts the server the way you chose. In background mode it writes the service definition, stops its own listener so the service can bind the port, starts the service and checks/api/healthfor up to 10 seconds. With--foregroundit keeps running until you press Ctrl+C.
The first device to pair becomes the team owner. On a phone or tablet, open Teams and choose Set up a new server to scan the code. The desktop apps take the URL through Teams › Paste URL. See QR Code Pairing.
The QR code is drawn with block characters on a local terminal. Over SSH (SSH_TTY set) or in a locale that is not UTF-8, it is drawn in plain ASCII instead. On a terminal too narrow for the code, only the URL is printed. --no-qr always prints only the URL, and --qr-invert swaps dark and light for a dark terminal without color support.
On a headless machine, pass --non-interactive: the interface prompt is skipped, the server listens on the first detected address, and the command waits for a device. Scan the code or paste the URL from another machine. The prompt is also skipped when stdin or stderr is not a terminal.
If no device pairs in time, or you press Ctrl+C, the command exits without writing config.json. Run pcmd again to start over with a new code; the TLS identity created in step 2 is reused.
Once the first device has paired, add further devices and people with pcmd invite or from the app.
Choosing the interface
Section titled “Choosing the interface”The server listens on one network interface: the one picked on the first run, or the first detected address with --non-interactive. It saves that address, and the QR code carries it. It never listens on every interface unless you ask for it. To pick an interface yourself, pass --host with an IP address of this machine, or 0.0.0.0 (:: to include IPv6) for every interface:
pcmd --host 10.8.0.1 # a VPN interface onlypcmd --host 127.0.0.1 # loopback only, reach it through an SSH tunnelpcmd --host 0.0.0.0 # every interfaceEvery data endpoint requires mutual TLS and a session token, whichever interface the server listens on.
Reaching the server from outside your network
Section titled “Reaching the server from outside your network”The QR code carries the address your devices connect to. By default that is the address the server listens on. --advertise-ip puts a different IP address or hostname in the code, and the first run saves it; pcmd invite, pcmd sync and pcmd rotate-server-cert reuse the saved address unless you pass another. IPv6 addresses are bracketed in the URLs.
-
A VPS whose public IP is on its network interface: pick that interface on the first run and allow TCP
8767in the firewall. -
Behind NAT (a home router, or a cloud VM that only sees a private address): forward TCP
8767to the interface the server listens on, and put the public address in the QR code before you pair. A dynamic-DNS name keeps working when your home IP changes:Terminal window pcmd --advertise-ip myhome.example.net
Background service
Section titled “Background service”| Platform | Init system | Service definition | Process output |
|---|---|---|---|
| Linux | systemd (user) | ~/.config/systemd/user/pocketcmd.service |
journal (journalctl --user -u pocketcmd) |
| Linux | OpenRC | /etc/init.d/pocketcmd, added to the default runlevel |
/var/log/pocketcmd.log |
| macOS | launchd | ~/Library/LaunchAgents/com.pocketcmd.server.plist |
~/.pocketcmd/server.log |
On Linux the installer uses systemd when systemctl is on the PATH, otherwise OpenRC when /sbin/openrc-run exists. Every service runs pcmd --foreground -p <port> --host <host> with the port and host from the run that installed it, and restarts it if it exits. The server’s own structured log is kept in state.db and read with pcmd logs.
- systemd: the unit restarts the server 5 seconds after an exit and is sandboxed: the file system is read-only except
~/.pocketcmd,/tmpis private and privilege escalation is blocked. Custom metric commands run inside the same sandbox. A user service stops when you log out unless lingering is enabled; enable it withloginctl enable-linger $USER.pcmd statusshows whether it is on. - OpenRC: OpenRC has no per-user services, so installing writes a system service and needs
sudo pcmd install. The service runs as the user who installed it, with that user’s~/.pocketcmd, so neither the server nor its metric commands run as root. - launchd: the agent has
RunAtLoadandKeepAliveset, so it starts when you log in and is restarted if it exits.
With no supported init system, the service install fails and the first run keeps the server running in the foreground. Run pcmd --foreground under the supervisor of your choice.
pcmd install refuses on a host where no device has paired yet, because the service would print pairing codes nobody sees. Run pcmd first, or pass --allow-unpaired.
Manage the service with:
pcmd status # running or stopped, PID, port, paired devices, mode, linger, pathspcmd stop # stop the service, keep it installedpcmd start # start it again and wait for /api/healthpcmd restart # stop and start; rewrites the definition only if it changedpcmd uninstall # stop and remove the service definitionpcmd install # write the service definition and start itIn background mode, running pcmd also installs the service if it is missing and starts it if it is stopped, then prints its status. pcmd status exits with 0 when the service runs, 2 when it is stopped and 3 when none is installed.
Containers
Section titled “Containers”There is no official container image. The Dockerfiles in the repository’s docker/ directory build the end-to-end test harness and are not meant for deployment. To run the server in your own container:
- Run
pcmd --foregroundas the container’s process, as a non-root user. - Persist
$HOME/.pocketcmdon a volume. It holds the server identity, and losing it means every device pairs again. - Publish port
8767/tcp. - Pass
--advertise-ipwith the host’s LAN address. The container’s own address is usually not reachable from your phone. - Start the first run with
--non-interactive, then scan the ASCII QR code or copy theOr copy:URL from the container’s output.
Verifying the installation
Section titled “Verifying the installation”pcmd --versionpcmd statuscurl -k https://127.0.0.1:8767/api/health/api/health is the only unauthenticated liveness endpoint and is rate-limited to 60 requests per minute per IP address. It answers 503 with "status":"degraded" when the server cannot reach state.db. -k is needed because the server certificate is self-signed.
Upgrading
Section titled “Upgrading”Run the install script again with your app’s API version, then restart the service so it runs the new binary:
pcmd backupcurl -fsSL https://pocketcmd.com/install.sh | sh -s -- --api 1pcmd restartPaired devices, users and sync data are kept; they live in ~/.pocketcmd, not in the binary. Releases follow semantic versioning: breaking changes to the CLI, the config file or the wire protocol only ship in a new major version, which is also a new API version. state.db migrates forward when the new binary starts, and an older binary refuses to open data written by a newer one, so keep the backup until the upgrade has worked.
Removing the server
Section titled “Removing the server”pcmd uninstall removes the service definition only. To remove everything:
pcmd uninstallrm "$(command -v pcmd)"rm -rf ~/.pocketcmdDeleting ~/.pocketcmd destroys the server identity, the team and every sync record stored on this server. Devices that were paired keep their local data.
Platforms
Section titled “Platforms”| Platform | Architectures | Service |
|---|---|---|
| macOS | amd64, arm64 | launchd agent |
| Linux | amd64, arm64 | systemd user service or OpenRC |
| Linux without an init system | amd64, arm64 | none, run with --foreground |
| Windows | n/a | not supported |