Quickstart
This page goes from an installed host to a running server you control: a configuration on disk, a validated compile, a server you can start, stop, and watch, and a verification step that proves the file server answered. It assumes the installation is done.
🧾 Before you start
Section titled “🧾 Before you start”The installer left a service running on port 80, and that service holds the
configuration in /etc/Pingclair/Pingclairfile. Stop it while you experiment so
the ports are free:
sudo pc service stopmkdir -p ~/demo/publiccd ~/demoecho '<h1>hello from ~/demo/public</h1>' > public/index.html1. ✍️ Write a configuration
Section titled “1. ✍️ Write a configuration”Create ~/demo/Pingclairfile:
{ admin 127.0.0.1:2019}
http://localhost:8080 { file_server ./public}Three things are worth naming. The unnamed block at the top holds global
options, and admin is what lets pingclair start, stop, and reload talk to
the running server. The site address carries the scheme, and http:// is what
forces plaintext; without it Pingclair treats localhost as a name and serves
HTTPS from its own certificate authority, which a plain HTTP client sees as an
empty reply (HTTPS). The file_server root is relative to the
working directory.
2. ✅ Validate before you run
Section titled “2. ✅ Validate before you run”pingclair validate✅ Configuration 'Pingclairfile' is valid!validate reads ./Pingclairfile by default and also detects ./Caddyfile. It
compiles the configuration and applies semantic checks, such as whether
certificate paths exist. Validation is not advisory: a configuration that fails
does not run, and a failing one prints the reason on the last line.
3. 🧭 Read what the configuration becomes
Section titled “3. 🧭 Read what the configuration becomes”pingclair adapt --pretty{ "debug": false, "servers": [ { "name": "localhost", "names": [ "localhost" ], "listen": [ "[::]:8080" ],The compiled JSON is the form the server actually runs. When a directive does
not behave as the documentation says, this is the first place to look. To see
what pingclair fmt would change in the file instead:
pingclair fmt --diff- file_server ./public+ file_server ./publicfmt prints the canonical form, which indents with two spaces.
4. 🚀 Run it
Section titled “4. 🚀 Run it”In the foreground, where the log stays attached to your terminal:
pingclair run Pingclairfile🚀 Starting Pingclair with config: Pingclairfile🚀 Starting Pingclair v0.2.0-rc.3📄 Loaded configuration from: Pingclairfile🔧 Configured 1 server(s)🔐 Auto HTTPS: enabledAdd --watch to reload the configuration every time you save it, which is the
development loop:
pingclair run --watch Pingclairfile♻️ Configuration reloaded successfully✅ Configuration reloaded completed successfully in 2.478622msOr run it in the background, where it survives your shell:
pingclair start -c Pingclairfile✅ Pingclair started in the background (pid 4432)pingclair start, stop, and reload reach the running server through the
Admin API, which is why the configuration above sets admin. pingclair run
does not need it.
5. 🔍 Verify
Section titled “5. 🔍 Verify”curl -i http://localhost:8080/HTTP/1.1 200 OKContent-Type: text/html; charset=utf-8Content-Length: 34Last-Modified: Tue, 22 Sep 2026 03:26:39 GMTETag: "22-6ab1f56f"Vary: Accept-EncodingAccept-Ranges: bytesserver: PingclairETag and Last-Modified mean the file server read the file from disk. The
body is public/index.html. To stop a background server:
pingclair stop✅ Pingclair stopped⚡ Servers in one command
Section titled “⚡ Servers in one command”Three subcommands serve without a configuration file, which is useful for trying something out or for a throwaway host:
pingclair file-server --listen :8081 --root ./publicpingclair reverse-proxy --from :8082 --to 127.0.0.1:8081pingclair respond --listen :8083 -s 200 -b "hello from respond"Each prints its listener on startup:
🚀 Starting file server on :8081 serving ./public (browse: false)🚀 Starting reverse proxy: :8082 -> ["127.0.0.1:8081"]Server address: [::]:8083Every request to :8082 is proxied to the file server on :8081, and :8083
answers with the body you passed. respond is for development only.
🔁 Move it into the service
Section titled “🔁 Move it into the service”The service runs /etc/Pingclair/Pingclairfile, so putting your configuration
there is what makes it survive a reboot:
sudo cp Pingclairfile /etc/Pingclair/Pingclairfilesudo pingclair validate /etc/Pingclair/Pingclairfilesudo pc service reloadcurl -i http://localhost/pc service reload asks the running server to read the file again, which the
unit does by sending SIGUSR1. pingclair reload reaches the same code through
the Admin API and also reports what the server thought of the file, which needs
the admin option from the global options block; and
sudo kill -USR1 "$(systemctl show -p MainPID --value pingclair)" does it with
neither.
Validate first either way, and read the answer afterwards: systemctl reload
reports only that the signal was delivered, so the server’s verdict — applied,
or refused with a reason — is on the unit’s status line and in the journal. A
refused reload leaves the previous configuration serving, which is the point of
refusing. Run it as a service is the long
version.
⚠️ When it does not work
Section titled “⚠️ When it does not work”Address already in use. The installer’s service still holds:80, or another process holds your port.sudo ss -ltnp | grep :80names the owner;sudo pc service stopfrees the default one.Empty reply from serveronhttp://localhost:8080. You are speaking plaintext to a TLS listener. Add thehttp://scheme to the site address, or talk to it withhttps://and trust the internal certificate.Cannot reach admin API at 127.0.0.1:2019. The configuration has noadminoption, so nothing is listening forpingclair stopandpingclair reload. Add it to the global options block, or stop the foreground process with Ctrl-C.curlhangs on a loopback address. A system proxy is intercepting the request. Repeat it withcurl --noproxy '*'.- Validation fails with
Unsupported feature. The directive is recognized but not implemented, and the message names the alternative, as inencode br: Brotli is not implemented for proxied responses, so the message points atencode zstd gzip.
🧭 Next steps
Section titled “🧭 Next steps”- HTTPS: certificates for a public name, from Let’s Encrypt or the internal authority.
- Run it as a service: the unit, its reload semantics, and its logs.
- Pingclairfile: the language itself, including matchers, snippets, and imports.
