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/ready. Automatic HTTPS requires a successful public response before reportingHarly is ready.
Generate configuration without launching
init writes configuration but does not pull images or start containers. Run:
--launch to init.
Resume an interrupted installation
After configuration has been saved, use Start or resume installation in the guided menu or run:resume checks the current installation
and then the exact ./harly default. You can preview the target with --dry-run.
Caddy is checked after the application is ready. It waits up to 15 minutes for
the public readiness endpoint to succeed, allowing time for DNS and automatic
HTTPS. Other services wait up to 90 seconds each. To change the per-service
timeout for launch or resume, add --timeout <seconds>.
The CLI reports elapsed time and the latest connection or HTTP status while
waiting. On timeout, it shows how to inspect Caddy’s logs and retry.
Install the CLI globally
After a successful interactive install, launch, or resume using npx, the CLI offers to install its current version globally. If accepted, you can runharly doctor, harly backup, and other commands from the installation
directory. Existing global installations are detected before prompting.
This optional step requires its own confirmation, even with --yes. Declining,
cancelling the prompt, or an npm permissions error keeps the deployment
successful. If npm’s global bin directory is outside your PATH, the CLI tells
you which directory to add. You can always keep using npx @harly/cli.
harly update upgrades the application. Upgrade the global CLI separately with
npm install -g @harly/cli@latest.
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.
Railway Config as Code
The repository also includesrailway.toml. Railway detects
this file automatically and applies its Dockerfile, web start command,
readiness path, and restart policy to the current service deployment. This is a
service-level configuration file, not a full project manifest: it does not
create PostgreSQL, the scheduler, or the migration service. Use the CLI API
flow above when you need the complete Harly topology.
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. The repository’s .do/app.yaml is the
canonical DigitalOcean App Platform spec and can also be used directly with
doctl apps create --spec .do/app.yaml or
doctl apps update APP_ID --spec .do/app.yaml; it provisions the multi-service
App Platform topology. 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
update from the installation directory, or from any directory inside it.
It reads the current stable release and moves this install there. It does
nothing when that version is already running. --to 0.2.1-beta.1 opts into a
prerelease. The flow creates a rollback archive, pulls that exact version,
runs the database migration once, restarts the services, and waits for
readiness.
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.