# ๐Ÿƒ 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](/start/install/) is done. ## ๐Ÿงพ 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: ```bash sudo pc service stop ``` ```bash mkdir -p ~/demo/public cd ~/demo echo '

hello from ~/demo/public

' > public/index.html ``` ## 1. โœ๏ธ Write a configuration Create `~/demo/Pingclairfile`: ```caddyfile { 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](/start/https/)). The `file_server` root is relative to the working directory. ## 2. โœ… Validate before you run ```bash pingclair validate ``` ```text โœ… 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 ```bash pingclair adapt --pretty ``` ```text { "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: ```bash pingclair fmt --diff ``` ```text - file_server ./public + file_server ./public ``` `fmt` prints the canonical form, which indents with two spaces. ## 4. ๐Ÿš€ Run it In the foreground, where the log stays attached to your terminal: ```bash pingclair run Pingclairfile ``` ```text ๐Ÿš€ 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: ```bash pingclair run --watch Pingclairfile ``` ```text โ™ป๏ธ Configuration reloaded successfully โœ… Configuration reloaded completed successfully in 2.478622ms ``` Or run it in the background, where it survives your shell: ```bash pingclair start -c Pingclairfile ``` ```text โœ… 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 ```bash curl -i http://localhost:8080/ ``` ```text 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: ```bash pingclair stop ``` ```text โœ… Pingclair stopped ``` ## โšก Servers in one command Three subcommands serve without a configuration file, which is useful for trying something out or for a throwaway host: ```bash 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: ```text ๐Ÿš€ 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. ## ๐Ÿ” Move it into the service The service runs `/etc/Pingclair/Pingclairfile`, so putting your configuration there is what makes it survive a reboot: ```bash 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](/start/service/#-what-a-reload-means) is the long version. ## โš ๏ธ 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 :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`. ## ๐Ÿงญ Next steps - [HTTPS](/start/https/): certificates for a public name, from Let's Encrypt or the internal authority. - [Run it as a service](/start/service/): the unit, its reload semantics, and its logs. - [Pingclairfile](/reference/pingclairfile/): the language itself, including matchers, snippets, and imports.