Installing Packyard
One-line install, interactive setup, and recipes for unattended automation, air-gapped hosts, reverse proxies, upgrades, and uninstall.
Updated April 27, 2026
The happy path is a single line:
curl -sSf https://get.packyard.dev/install.sh | shThat downloads the right binary for your host, verifies the SHA-256 checksum, drops it into a PATH directory, then hands off to packyard init — the interactive configuration wizard. Most people are done here.
The rest of this page is for everything else: unattended installs, air-gapped hosts, reverse-proxy setup, upgrades, uninstall.
What the install script actually does
- Preflight — checks
curl/wget,tar,sha256sum/shasum. Requires Linux amd64 (x86_64). arm64 and macOS build from source — see the repo README. - Download — queries GitHub's
/releasesAPI for the most recent release tag (including pre-releases), then downloadspackyard-<tag>-linux-amd64.tar.gzandSHA256SUMSfrom that release. - Verify — hashes the tarball and compares against the expected value in
SHA256SUMS. Any mismatch is a hard fail. - Install — extracts the binary, places it in
/usr/local/bin/packyard(root install) or~/.local/bin/packyard(non-root).chmod 0755. - Handoff —
exec packyard initwith any arguments you passed after--.
Nothing else. No env files, no services, no DB writes — all of that lives inside packyard init and can be inspected independently.
Pinning to a specific version
By default the installer grabs the most recent release. To pin:
curl -sSf https://get.packyard.dev/install.sh | sh -s -- --version v1.0.0-beta.1See the Releases page for available tags.
Unattended install
Every wizard prompt has a matching flag and env var. Pass --unattended to skip the TUI entirely:
curl -sSf https://get.packyard.dev/install.sh | sh -s -- \
-- --unattended \
--port 9090 \
--url https://repo.example.com \
--db sqlite \
--admin-email [email protected]Everything after the -- gets forwarded to packyard init. Flag reference (see also packyard init --help):
| Flag | Env var | Purpose |
|---|---|---|
--unattended |
PACKYARD_UNATTENDED |
Skip prompts, fail on missing required values |
--config PATH |
PACKYARD_CONFIG_FILE |
Where to write the env file |
--data-dir PATH |
PACKYARD_DATA_DIR |
SQLite DB + local storage live here |
--port N |
PACKYARD_PORT |
HTTP listen port (default 9090) |
--url URL |
PACKYARD_BASE_URL |
Public URL, embedded in Composer dist URLs |
--db sqlite|mysql|postgres |
PACKYARD_DB_DRIVER |
Database driver |
--db-host, --db-port, --db-name, --db-user, --db-password, --db-sslmode |
PACKYARD_DB_* |
MySQL/Postgres connection |
--storage local|s3 |
PACKYARD_STORAGE_TYPE |
Where package zips live |
--storage-path PATH |
PACKYARD_STORAGE_LOCAL_PATH |
Local storage directory |
--s3-bucket, --s3-region, --s3-endpoint, --s3-access-key, --s3-secret-key |
PACKYARD_S3_* |
S3 connection |
--admin-email EMAIL |
PACKYARD_ADMIN_EMAIL |
Admin user email |
--admin-password PASS |
PACKYARD_ADMIN_PASSWORD |
Admin password (auto-generated when omitted) |
--no-service |
PACKYARD_NO_SERVICE |
Skip systemd/launchd service install |
--force-port |
PACKYARD_FORCE_PORT |
Skip port-in-use check |
Air-gapped install
The install script just wraps a download-verify-place cycle. On a host without internet:
# On a networked machine (pick a tag from the Releases page):
TAG=v1.0.0-beta.1
curl -LO "https://github.com/usepackyard/packyard/releases/download/${TAG}/packyard-${TAG}-linux-amd64.tar.gz"
curl -LO "https://github.com/usepackyard/packyard/releases/download/${TAG}/SHA256SUMS"
# Transfer both files to the air-gapped host, then:
sha256sum -c SHA256SUMS --ignore-missing
tar -xzf packyard-${TAG}-linux-amd64.tar.gz
sudo install -m 0755 packyard /usr/local/bin/packyard
# Continue with the interactive wizard, or use --unattended as above.
sudo packyard initUpgrade
Re-run the installer. The download-verify-install part is idempotent; packyard init detects the existing config and leaves it alone:
curl -sSf https://get.packyard.dev/install.sh | sh
# or, to pin:
curl -sSf https://get.packyard.dev/install.sh | sh -s -- --version v1.0.0-beta.2If you installed the systemd unit, install.sh replaces the binary and then hands off to packyard init, which re-applies migrations (no-op if already applied) and re-creates the service unit. systemctl restart packyard after the install completes to pick up the new binary.
Reverse proxy
Packyard binds HTTP only. For anything public, put a TLS-terminating proxy in front. Three ready-to-copy snippets:
Caddy
repo.example.com {
reverse_proxy 127.0.0.1:9090
}nginx
server {
listen 443 ssl http2;
server_name repo.example.com;
ssl_certificate /etc/letsencrypt/live/repo.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/repo.example.com/privkey.pem;
# Composer uploads can be 100 MB; let them through.
client_max_body_size 100m;
location / {
proxy_pass http://127.0.0.1:9090;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}After putting Packyard behind a reverse proxy, set PACKYARD_TRUSTED_PROXIES in /etc/packyard/packyard.env to the proxy's CIDR (e.g. 127.0.0.1/32 for a local proxy). Without this, per-IP rate limiting keys off the proxy's IP and can be bypassed by header injection from an attacker.
Traefik
http:
routers:
packyard:
rule: "Host(`repo.example.com`)"
entryPoints: [websecure]
service: packyard
tls:
certResolver: letsencrypt
services:
packyard:
loadBalancer:
servers: [{ url: "http://127.0.0.1:9090" }]Uninstall
packyard init --uninstallStops and disables the systemd/launchd service, removes the binary, env file, and system user. The data directory (SQLite DB + package zips) is preserved by default — pass --purge-data to wipe it too.
Administering without the dashboard
Some ops tasks need to happen before the server is up or when the dashboard is down. The binary has a small set of admin subcommands for exactly that:
# Create a user directly in the DB (works without the server running):
packyard admin user create --email [email protected] --super-admin
# Validate config without starting anything:
packyard check-config
# Probe the DB:
packyard check-db
# Run pending migrations:
packyard migrate
# Check the server's health:
packyard healthcheckAll of these read the env file at PACKYARD_CONFIG_FILE (or the systemd EnvironmentFile).
Getting help
- Full CLI reference:
packyard --helpandpackyard <subcommand> --help. - Architecture and development notes: AGENTS.md.
- Bugs / requests: github.com/usepackyard/packyard/issues.