Skip to content

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.

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:

Terminal window
sudo pc service stop
Terminal window
mkdir -p ~/demo/public
cd ~/demo
echo '<h1>hello from ~/demo/public</h1>' > public/index.html

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.

Terminal window
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”
Terminal window
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:

Terminal window
pingclair fmt --diff
- file_server ./public
+ file_server ./public

fmt prints the canonical form, which indents with two spaces.

In the foreground, where the log stays attached to your terminal:

Terminal window
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: enabled

Add --watch to reload the configuration every time you save it, which is the development loop:

Terminal window
pingclair run --watch Pingclairfile
♻️ Configuration reloaded successfully
✅ Configuration reloaded completed successfully in 2.478622ms

Or run it in the background, where it survives your shell:

Terminal window
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.

Terminal window
curl -i http://localhost:8080/
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 34
Last-Modified: Tue, 22 Sep 2026 03:26:39 GMT
ETag: "22-6ab1f56f"
Vary: Accept-Encoding
Accept-Ranges: bytes
server: Pingclair

ETag and Last-Modified mean the file server read the file from disk. The body is public/index.html. To stop a background server:

Terminal window
pingclair stop
✅ Pingclair stopped

Three subcommands serve without a configuration file, which is useful for trying something out or for a throwaway host:

Terminal window
pingclair file-server --listen :8081 --root ./public
pingclair reverse-proxy --from :8082 --to 127.0.0.1:8081
pingclair 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: [::]:8083

Every request to :8082 is proxied to the file server on :8081, and :8083 answers with the body you passed. respond is for development only.

The service runs /etc/Pingclair/Pingclairfile, so putting your configuration there is what makes it survive a reboot:

Terminal window
sudo cp Pingclairfile /etc/Pingclair/Pingclairfile
sudo pingclair validate /etc/Pingclair/Pingclairfile
sudo pc service reload
curl -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.

  • Address already in use. The installer’s service still holds :80, or another process holds your port. sudo ss -ltnp | grep :80 names the owner; sudo pc service stop frees the default one.
  • Empty reply from server on http://localhost:8080. You are speaking plaintext to a TLS listener. Add the http:// scheme to the site address, or talk to it with https:// and trust the internal certificate.
  • Cannot reach admin API at 127.0.0.1:2019. The configuration has no admin option, so nothing is listening for pingclair stop and pingclair reload. Add it to the global options block, or stop the foreground process with Ctrl-C.
  • curl hangs on a loopback address. A system proxy is intercepting the request. Repeat it with curl --noproxy '*'.
  • Validation fails with Unsupported feature. The directive is recognized but not implemented, and the message names the alternative, as in encode br: Brotli is not implemented for proxied responses, so the message points at encode zstd gzip.
  • 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.