{ pkgs, service_configs, lib, ... }: { imports = [ (lib.serviceMountWithZpool "traccar" service_configs.zpool_ssds [ "/var/lib/traccar" ]) (lib.serviceFilePerms "traccar" [ "Z /var/lib/traccar 0700 traccar traccar" ]) ]; users.users.traccar = { isSystemUser = true; group = "traccar"; home = "/var/lib/traccar"; description = "Traccar GPS Tracking"; }; users.groups.traccar = { }; # PostgreSQL database (auto-created, peer auth via Unix socket) services.postgresql = { ensureDatabases = [ "traccar" ]; ensureUsers = [ { name = "traccar"; ensureDBOwnership = true; } ]; }; services.traccar = { enable = true; # The JDBC URL contains '$' (Java inner class) and '&' (query param # separator) which break the NixOS module's XML generator + envsubst. # Route it through environmentFile so envsubst replaces $TRACCAR_DB_URL # with the literal value, and use & for valid XML (the XML parser # decodes it back to & for JDBC). environmentFile = pkgs.writeText "traccar-db-env" '' TRACCAR_DB_URL=jdbc:postgresql:///traccar?socketFactory=org.newsclub.net.unix.AFUNIXSocketFactory$FactoryArg&socketFactoryArg=${service_configs.postgres.socket}/.s.PGSQL.5432 ''; settings = { web.port = toString service_configs.ports.private.traccar_web.port; # PostgreSQL via Unix socket (peer auth, junixsocket is bundled) database = { driver = "org.postgresql.Driver"; url = "$TRACCAR_DB_URL"; user = "traccar"; password = ""; }; # Only enable OsmAnd protocol (phone app). Prevents Traccar from # opening 200+ default protocol ports that conflict with other services. protocols.enable = "osmand"; osmand.port = toString service_configs.ports.private.traccar_tracking.port; }; }; # Disable DynamicUser so we can use peer auth with PostgreSQL systemd.services.traccar = { after = [ "postgresql.service" ]; requires = [ "postgresql.service" ]; serviceConfig = { DynamicUser = lib.mkForce false; User = "traccar"; Group = "traccar"; }; }; # Route tracking requests (OsmAnd protocol) through Caddy for TLS. # The phone app connects via HTTPS instead of a separate plain port. services.caddy.virtualHosts."${service_configs.traccar.domain}".extraConfig = '' @tracking query id=* reverse_proxy @tracking :${toString service_configs.ports.private.traccar_tracking.port} reverse_proxy :${toString service_configs.ports.private.traccar_web.port} ''; }