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.