What you will learn
- Check a host before installing Harly.
- Generate and launch a local Docker deployment.
- Automate the installer from CI or an agent without unexpected prompts.
- Provision Railway or prepare Fly.io and DigitalOcean deployments.
- Interpret JSON results, exit codes, and readiness failures.
Prerequisites
For a local VPS installation, install:- Node.js 20.12 or newer.
- Docker Engine 24 or newer.
- Docker Compose 2.20 or newer.
- At least 5 GB of free disk space by default.
- A public DNS record when using Caddy or an external HTTPS proxy.
Install Harly locally
Run the guided installer
- Validates Docker, Compose, ports, disk, memory, firewall, and DNS.
- Resolves a pinned Harly release image.
- Creates
.env,compose.yaml,Caddyfile,harly.config.json, and local operator documentation. - Generates independent runtime secrets and protects
.envwith mode0600. - Optionally pulls images and launches the services.
- Waits for PostgreSQL, migrations, the web app, the scheduler, and Caddy.
- Checks
/api/health/readyand reportsHarly is readyonly after it succeeds.
Generate configuration without launching
init writes configuration but does not pull images or start containers. Run:
--launch to init.
Configure installer inputs
Public values can be passed as flags:init ./harly and init --output-dir ./harly refer to the same target. If
both are provided with different paths, the command fails instead of choosing
one silently.
S3 values should come from the environment:
Automate the CLI
Global flags work before or after a subcommand when unambiguous:--non-interactive in CI and agent workflows. It never prompts; missing
values fail before host or provider operations begin.
JSON output
--json disables prompts and loaders. stdout contains exactly one JSON document
for success, failure, and cancellation. Human diagnostics, when enabled, are
written to stderr only.
Example dry run:
INVALID_ARGUMENT, MISSING_SECRET,
PREFLIGHT_FAILED, READINESS_TIMEOUT, and PARTIAL_PROVISIONING.
Secrets and stdin
Never pass secrets as ordinary flags. Use environment variables or one explicit stdin source per execution:Dry runs
--dry-run --json resolves the configuration and lists artifacts and planned
remote operations. It does not:
- Write files or create directories.
- Download images.
- Run Docker Compose.
- Call provider APIs.
- Create cloud resources.
- Save secrets.
Deploy to managed platforms
Railway
Railway is the fully automated cloud provider. Set secrets through the environment and run:status: "ready".
Railway secrets are not saved locally by default. Add --save-env only when a
local copy is explicitly required:
0600, and the CLI prints a warning. If Railway
fails after creating resources, JSON includes status: "failed",
error.code: "PARTIAL_PROVISIONING", resourcesCreated, and recovery steps.
Resources are not deleted automatically.
When --url or --domain is supplied, the generated Railway domain remains
the verified deployment URL. The requested custom domain is returned as
requestedUrl with an explicit follow-up step to attach and verify it in
Railway.
Fly.io and DigitalOcean
These providers currently use a preparation flow and finish withstatus: "ready-to-deploy":
fly.toml or app.yaml, validates the resolved configuration,
and prints the exact provider commands still required. It does not create
provider resources. Use --save-env if you explicitly need a local 0600
secrets file.
Operate an installation
Check readiness
doctor --fix only in an interactive terminal. It can restart stopped
services or bring the Compose project back up.
Back up before updates
Restore or uninstall
--remove-data option for uninstall permanently removes PostgreSQL and upload
volumes; it is intentionally separate from the default uninstall behavior.
Exit codes
Troubleshooting
Caddy cannot bind ports 80 or 443
Check the process reported by the installer. Stop or reconfigure the existing proxy, or use--proxy external and forward traffic to the Harly host port.