Files
Dave AndersonandWill Norris 41e9dc91ec flake.nix: don't wait for network-online.target to start golink
golink is a tsnet service, and Tailscale's data plane is very good at monitoring for changes to network state and reacting to them. As such, it doesn't need to wait for network-online.target, it can start up concurrently with everything else and shorten the long pole of bootup.

Additionally, network-online.target is an antipattern in systemd that almost never means what people want it to mean. "Online" has no precise definition, and (I posit) almost never matches the ideal version of "online" that people have in their mind. It specifically does _not_ mean that the network functions, or that any particular flavor of connectivity exists. A machine behind a captive portal is "online" for the purposes of bootup, and so is a machine that got a DHCP lease but is having all its packets blackholed. Systemd has a whole page of documentation on "what does online even mean", because this is such a recurring point of confusion: https://systemd.io/NETWORK_ONLINE/

It is almost always wrong to depend on network-online.target, and I wish it was named slow-down-boot-for-no-reason.target, which is how most downstream programs use it.

</rant>, the point is, golink is a well-behaved service that reacts to connectivity changes on the fly (it has to, that's what tailscale does), and so can be started concurrently with networking and other services.
2024-04-25 09:20:28 -07:00

162 lines
4.3 KiB
Nix

{
description = "golink - A private shortlink service for tailnets";
inputs = {
nixpkgs.url = "nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs =
{ self
, nixpkgs
, flake-utils
, ...
}:
let
golinkVersion =
if (self ? shortRev)
then self.shortRev
else "dev";
in
{
overlay = final: prev:
let
pkgs = nixpkgs.legacyPackages.${prev.system};
in
rec {
golink = pkgs.buildGo122Module rec {
pname = "golink";
version = golinkVersion;
src = pkgs.nix-gitignore.gitignoreSource [ ] ./.;
vendorHash = "sha256-PWeQNlIMvhGAFDVwN8fp0B11Loi6zbm1Pds/CKLeuvA="; # SHA based on vendoring go.mod
};
};
}
// flake-utils.lib.eachDefaultSystem
(system:
let
pkgs = import nixpkgs {
overlays = [ self.overlay ];
inherit system;
};
in
rec {
# `nix develop`
devShell = pkgs.mkShell { buildInputs = [ pkgs.go_1_21 ]; };
# `nix build`
packages = with pkgs; {
inherit golink;
};
defaultPackage = pkgs.golink;
# `nix run`
apps.golink = flake-utils.lib.mkApp {
drv = packages.golink;
};
defaultApp = apps.golink;
overlays.default = self.overlay;
})
// {
nixosModules.default =
{ pkgs
, lib
, config
, ...
}:
let
cfg = config.services.golink;
in
{
options = with lib; {
services.golink = {
enable = mkEnableOption "Enable golink";
package = mkOption {
type = types.package;
description = ''
golink package to use
'';
default = pkgs.golink;
};
dataDir = mkOption {
type = types.path;
default = "/var/lib/golink";
description = "Path to data dir";
};
user = mkOption {
type = types.str;
default = "golink";
description = "User account under which golink runs.";
};
group = mkOption {
type = types.str;
default = "golink";
description = "Group account under which golink runs.";
};
databaseFile = mkOption {
type = types.path;
default = "/var/lib/golink/golink.db";
description = "Path to SQLite database";
};
tailscaleAuthKeyFile = mkOption {
type = types.path;
description = "Path to file containing the Tailscale Auth Key";
};
verbose = mkOption {
type = types.bool;
default = false;
};
};
};
config = lib.mkIf cfg.enable {
users.users."${cfg.user}" = {
home = cfg.dataDir;
createHome = true;
group = "${cfg.group}";
isSystemUser = true;
isNormalUser = false;
description = "user for golink service";
};
users.groups."${cfg.group}" = { };
systemd.services.golink = {
enable = true;
script =
let
args =
[
"--sqlitedb ${cfg.databaseFile}"
]
++ lib.optionals cfg.verbose [ "--verbose" ];
in
''
${lib.optionalString (cfg.tailscaleAuthKeyFile != null) ''
export TS_AUTHKEY="$(head -n1 ${lib.escapeShellArg cfg.tailscaleAuthKeyFile})"
''}
${cfg.package}/bin/golink ${builtins.concatStringsSep " " args}
'';
wantedBy = [ "multi-user.target" ];
serviceConfig = {
User = cfg.user;
Group = cfg.group;
Restart = "always";
RestartSec = "15";
WorkingDirectory = "${cfg.dataDir}";
};
};
};
};
};
}