Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

NixOS module

Import the flake module and enable the service:

{
  imports = [ inputs.piqueld.nixosModules.default ];
  services.piqueld.enable = true;
  users.users.alice.extraGroups = [ "piqueld" ];
}

The default package embeds the dashboard. services.piqueld.package can select inputs.piqueld.packages.${pkgs.system}.daemon for API-only operation. The default module installs the CLI when the daemon is enabled; set programs.piquelctl.enable = false to omit it. For a remote-management machine with only the CLI, import inputs.piqueld.nixosModules.piquelctl and enable programs.piquelctl. This module does not enable Docker or create the daemon service or user.

Connection profiles use the same typed-settings style as the daemon:

{
  imports = [ inputs.piqueld.nixosModules.piquelctl ];
  programs.piquelctl = {
    enable = true;
    settings.default = "production";
    settings.profiles.production = {
      url = "http://127.0.0.1:7845";
      timeout = "2m";
    };
  };
}

Each profile must set exactly one of socket or url; timeout is optional. settings.default, also optional, names the profile piquelctl uses when neither --profile nor PIQUELD_PROFILE selects one. The module writes /etc/piqueld/profiles.toml and installs the unwrapped CLI. Every user and every CLI build, including cargo run -p piquelctl, discovers these profiles without environment setup. User profiles can add or replace named system profiles; --profiles-file and PIQUELD_PROFILES_FILE bypass discovery. Run piquelctl profiles to see the effective names and endpoints.

For nix-darwin, import inputs.piqueld.darwinModules.piquelctl and use the same programs.piquelctl options. The shared CLI module does not require the daemon.

When migrating from the wrapper-based module, rebuild the machine configuration and remove old PIQUELD_PROFILES_FILE exports or custom wrappers unless you intentionally want an exclusive file override. Remove duplicate TOML generation and per-user profile symlinks/activation scripts used to expose system profiles; the generated system file now serves all users. Keep intentional user overrides in the user configuration file. Profile settings remain in the public Nix store and must not contain credentials.

The module runs piqueld check-config on the generated file while building the system, so an invalid setting fails nixos-rebuild build instead of piqueld.service. Credentials are not read during the check. Set services.piqueld.checkConfig = false; when the package cannot run on the build machine, such as when cross-compiling.

dataDir defaults to /var/lib/piqueld, with mode 0700, holding piqueld.db. runtimeDir defaults to /run/piqueld, prepared by systemd with mode 0750. The socket is /run/piqueld/piqueld.sock, owned by piqueld:piqueld with mode 0660. After logging in again to refresh group membership, users can connect to the socket without sudo. Membership permits only a socket connection; run piquelctl login to authenticate an account before piquelctl status or other API operations. It grants no direct access to private state or permission to replace the socket. Root (sudo piquelctl …) and the piqueld user act as the host operator without logging in. Only the existing piqueld.service is needed; no proxy or socket unit is required.

Custom runtimeDir values must be dedicated directories below /run. Configure CLI profiles or --socket explicitly when overriding this default. The module enables Docker and grants the service user Docker access, which is host-administrative authority. The service starts after Docker but does not require it: a Docker failure or restart leaves piqueld serving, and it resumes deploying once Docker is back. No firewall ports are opened. TCP is disabled by default. Use settings.server.listen_mode = "localhost"; for local HTTP, or configure Tailscale explicitly:

services.tailscale.enable = true;
services.piqueld.settings.server = {
  listen_mode = "tailscale"; # or "both" to also serve localhost
  port = 7845;
};

Join the host to your tailnet separately. The module supplies the Tailscale CLI and orders piqueld after tailscaled for these modes; it does not enable or configure Tailscale itself. Ordering does not guarantee connectivity: if Tailscale is unavailable at startup, piqueld warns and requires a restart to activate remote listening. Ensure your firewall and tailnet policy permit the selected port on the Tailscale interface. All callers must authenticate. Configure settings.auth.public_url with a stable HTTPS hostname and terminate TLS externally; see your own reverse proxy.

For HTTPS without a proxy, let piqueld join the tailnet as its own node instead. piqueld runs a dedicated, unprivileged tailscaled from services.tailscale.package rather than using the host’s, and its state lives in dataDir. The host does not need services.tailscale.enable:

services.piqueld.settings.tailscale = {
  enabled = true;
  hostname = "piqueld"; # https://piqueld.<tailnet>.ts.net
  auth_key_file = config.age.secrets.piqueld-ts-auth-key.path; # first login only
};

settings.auth.public_url defaults to the node’s HTTPS URL. See the tailnet node for details.

settings declares typed options for server.listen_mode, server.port, server.allowed_hosts, auth.public_url, auth.max_token_days, tailscale.enabled, tailscale.hostname, tailscale.auth_key_file, tailscale.https_port, tailscale.socket, docker.socket, docker.auto_initialize_swarm, ingress.enabled, ingress.public_addresses, ingress.acme.directory, ingress.acme.email, ingress.private.enabled, ingress.private.hostname, ingress.private.auth_key_file (passed as a systemd credential, like the other _file settings), ingress.private.dashboard_hostname (which also puts the Tailscale CLI on the service’s PATH), ingress.tunnel.enabled, ingress.tunnel.credentials_file (also a systemd credential, required while the tunnel is enabled), ingress.webhook_hostname, dns.providers, all three reconciliation intervals/timeouts, the retention periods, both build_history limits, the five previews limits, snapshots.max_upload_bytes, checkouts.max_bytes, both images settings, telemetry.endpoint, telemetry.headers_file (a systemd credential), and telemetry.containers, with the daemon’s defaults. Reconciliation values must be 1–86400 seconds; retention values are nonnegative days, with zero disabling pruning. Unknown settings are rejected.

Notification destinations are managed with piquelctl notification or the dashboard, not the module; the former settings.notifications options are removed.

Telemetry export pushes traces, metrics and logs to an OTLP/gRPC collector, along with the logs and resource usage of every container unless containers = false. A collector token goes in headers_file, which systemd passes to the daemon as the otlp-headers credential:

services.piqueld.settings.telemetry = {
  endpoint = "https://otel.example.com:4317"; # or "unix:///run/otel/otlp.sock"
  headers_file = config.age.secrets.otlp-headers.path; # `authorization: Bearer …`
  containers = true; # the default
};

DNS providers take their credentials only as files:

services.piqueld.settings = {
  ingress.acme.email = "[email protected]"; # optional
  dns.providers = [
    {
      kind = "cloudflare";
      api_token_file = config.age.secrets.cloudflare-dns-token.path;
      manage_records = true; # optional: maintain routes' DNS records
    }
    {
      kind = "ovh";
      endpoint = "ovh-eu";
      application_key_file = config.age.secrets.ovh-application-key.path;
      application_secret_file = config.age.secrets.ovh-application-secret.path;
      consumer_key_file = config.age.secrets.ovh-consumer-key.path;
    }
  ];
};

Settings enter the world-readable Nix store, so credentials are only accepted as _file settings taking a host path, such as an agenix or sops-nix secret: the Tailscale auth key as auth_key_file, and DNS provider credentials as their _file settings. The module passes every _file path to systemd LoadCredential=: systemd reads the file as root and hands the service a private copy, so the secret can stay root-only. Restart piqueld after a secret changes.

The module always supplies server.data_dir from dataDir and server.runtime_dir from runtimeDir. It does not configure a registry, Traefik, TLS termination or an external UI directory. Git, SSH, and Docker executables are present on the service PATH. Configure SSH credentials and known hosts for the service user, not the interactive operator; host home directories are protected.

Nix packages and development shells support x86_64 Linux and aarch64 macOS, where only the CLI package is available. Configuration and package changes require rebuilding NixOS; application state remains under dataDir.