feat: add Yami dev stack module (on-demand gitea+postgres+docker+runner) #3
@@ -7,6 +7,7 @@
|
||||
./desktop.nix
|
||||
./nvidia.nix
|
||||
./shell.nix
|
||||
# ./yami-dev-stack.nix # uncomment to enable Yami dev stack, then set services.yami-dev-stack below
|
||||
];
|
||||
|
||||
nix = {
|
||||
@@ -94,7 +95,8 @@
|
||||
programs.gamemode.enable = true;
|
||||
|
||||
services.tailscale.enable = true;
|
||||
virtualisation.docker.enable = true;
|
||||
# Docker is now managed by yami-dev-stack.nix when that module is enabled.
|
||||
# services.yami-dev-stack = { enable = true; user = "chexit"; };
|
||||
|
||||
system.stateVersion = "25.11";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.yami-dev-stack;
|
||||
in
|
||||
{
|
||||
options.services.yami-dev-stack = {
|
||||
enable = mkEnableOption "Yami development stack (gitea, postgres, docker, runner)";
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
description = "User that will own runner token and be added to docker group";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
# ── Systemd target ─────────────────────────────────────────────────────────
|
||||
# Manual-only: nothing in wantedBy, so it never starts on boot.
|
||||
systemd.targets.yami-dev = {
|
||||
description = "Yami development stack (gitea, postgres, docker, runner)";
|
||||
wantedBy = [ ];
|
||||
};
|
||||
|
||||
# ── PostgreSQL 16 ──────────────────────────────────────────────────────────
|
||||
services.postgresql = {
|
||||
enable = true;
|
||||
package = pkgs.postgresql_16;
|
||||
ensureDatabases = [ "gitea" ];
|
||||
ensureUsers = [
|
||||
{
|
||||
name = "gitea";
|
||||
ensureDBOwnership = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
# Pull postgres under yami-dev.target; stop it when target stops.
|
||||
systemd.services.postgresql = {
|
||||
wantedBy = mkForce [ "yami-dev.target" ];
|
||||
partOf = [ "yami-dev.target" ];
|
||||
};
|
||||
|
||||
# ── Gitea ──────────────────────────────────────────────────────────────────
|
||||
services.gitea = {
|
||||
enable = true;
|
||||
appName = "Yami Git";
|
||||
|
||||
database = {
|
||||
type = "postgres";
|
||||
socket = "/run/postgresql";
|
||||
name = "gitea";
|
||||
user = "gitea";
|
||||
};
|
||||
|
||||
settings = {
|
||||
server = {
|
||||
DOMAIN = "localhost";
|
||||
ROOT_URL = "http://localhost:3000/";
|
||||
HTTP_ADDR = "0.0.0.0";
|
||||
|
|
||||
HTTP_PORT = 3000;
|
||||
SSH_PORT = 2222;
|
||||
|
Gitea built-in SSH server typically listens on Gitea built-in SSH server typically listens on `SSH_LISTEN_PORT` (default 22); setting only `SSH_PORT = 2222` usually affects clone URL display, not the actual bind port. With `START_SSH_SERVER = true` this may try to bind to 22 and fail (or require extra privileges). Set `SSH_LISTEN_PORT = 2222` (and optionally `SSH_LISTEN_HOST`) alongside `SSH_PORT` to ensure it actually listens on 2222.
```suggestion
SSH_PORT = 2222;
SSH_LISTEN_PORT = 2222;
```
|
||||
START_SSH_SERVER = true;
|
||||
LFS_START_SERVER = true;
|
||||
};
|
||||
|
||||
service = {
|
||||
DISABLE_REGISTRATION = true;
|
||||
DEFAULT_KEEP_EMAIL_PRIVATE = true;
|
||||
};
|
||||
|
||||
repository = {
|
||||
DEFAULT_BRANCH = "main";
|
||||
ENABLE_PUSH_CREATE_USER = true;
|
||||
ENABLE_PUSH_CREATE_ORG = true;
|
||||
};
|
||||
|
||||
actions = {
|
||||
ENABLED = true;
|
||||
DEFAULT_ACTIONS_URL = "github";
|
||||
LOG_RETENTION_DAYS = 14;
|
||||
ARTIFACT_RETENTION_DAYS = 30;
|
||||
};
|
||||
|
||||
packages = {
|
||||
ENABLED = true;
|
||||
};
|
||||
|
||||
session = {
|
||||
COOKIE_SECURE = false;
|
||||
};
|
||||
|
||||
"cron.git_gc_repos" = {
|
||||
ENABLED = true;
|
||||
SCHEDULE = "@every 72h";
|
||||
ARGS = "--aggressive --prune=now";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.gitea = {
|
||||
wantedBy = mkForce [ "yami-dev.target" ];
|
||||
partOf = [ "yami-dev.target" ];
|
||||
after = [ "postgresql.service" ];
|
||||
requires = [ "postgresql.service" ];
|
||||
};
|
||||
|
||||
# ── Docker ─────────────────────────────────────────────────────────────────
|
||||
virtualisation.docker = {
|
||||
enable = true;
|
||||
enableOnBoot = false;
|
||||
};
|
||||
|
||||
systemd.services.docker = {
|
||||
wantedBy = mkForce [ "yami-dev.target" ];
|
||||
partOf = [ "yami-dev.target" ];
|
||||
};
|
||||
|
||||
|
For “manual-only” Docker, overriding only For “manual-only” Docker, overriding only `systemd.services.docker.wantedBy` may not be sufficient if `docker.socket` is still enabled (socket activation can start dockerd without `yami-dev.target`). Verify whether `virtualisation.docker.enableOnBoot = false` disables the socket; if not, also adjust `systemd.sockets.docker.wantedBy` (or explicitly disable the socket) so Docker can’t start outside `yami-dev.target`.
```suggestion
systemd.sockets.docker = {
wantedBy = mkForce [ "yami-dev.target" ];
partOf = [ "yami-dev.target" ];
};
```
|
||||
users.users.${cfg.user}.extraGroups = [ "docker" ];
|
||||
|
||||
|
`users.users.${cfg.user}.extraGroups = [ "docker" ];` will create/modify that user entry from this module. If `cfg.user` isn’t already defined elsewhere, this can lead to an invalid/incomplete user definition or unexpected user creation. Consider adding an assertion that the user exists (`builtins.hasAttr cfg.user config.users.users`) and use `mkAfter` to append the group instead of redefining the list.
```suggestion
assertions = [
{
assertion = builtins.hasAttr cfg.user config.users.users;
message = "services.yami-dev-stack.user must refer to an existing user defined in users.users";
}
];
users.users.${cfg.user}.extraGroups = mkAfter [ "docker" ];
```
|
||||
# ── Gitea Actions Runner ───────────────────────────────────────────────────
|
||||
services.gitea-actions-runner.instances.default = {
|
||||
enable = true;
|
||||
name = "home-runner";
|
||||
url = "http://localhost:3000";
|
||||
tokenFile = "/var/lib/gitea-runner/token";
|
||||
labels = [
|
||||
|
`services.gitea-actions-runner` will fail to start if `tokenFile` doesn’t exist yet, which makes `yami-up` likely to report a failed unit on first run. Consider adding a systemd `ConditionPathExists=` (or similar gating) for `gitea-runner-default` so the rest of the target can start cleanly before the token is provisioned.
|
||||
"ubuntu-latest:docker://node:20"
|
||||
"native:host"
|
||||
];
|
||||
};
|
||||
|
||||
systemd.services."gitea-runner-default" = {
|
||||
wantedBy = mkForce [ "yami-dev.target" ];
|
||||
partOf = [ "yami-dev.target" ];
|
||||
after = [ "gitea.service" "docker.service" ];
|
||||
requires = [ "gitea.service" "docker.service" ];
|
||||
};
|
||||
|
||||
# Directory for the runner token file; owner matches the runner service user.
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /var/lib/gitea-runner 0700 gitea-runner gitea-runner -"
|
||||
];
|
||||
|
||||
# ── Shell aliases ──────────────────────────────────────────────────────────
|
||||
environment.shellAliases = {
|
||||
yami-up = "sudo systemctl start yami-dev.target && echo 'Gitea → http://localhost:3000'";
|
||||
yami-down = "sudo systemctl stop yami-dev.target && echo 'Yami dev stack stopped'";
|
||||
yami-status = "systemctl status yami-dev.target gitea postgresql docker gitea-runner-default --no-pager";
|
||||
yami-logs = "journalctl -u gitea -u gitea-runner-default -f";
|
||||
};
|
||||
|
||||
# ── Firewall ───────────────────────────────────────────────────────────────
|
||||
# Merged with existing rules (Steam etc.); does not overwrite them.
|
||||
networking.firewall.allowedTCPPorts = [ 3000 2222 ];
|
||||
};
|
||||
}
|
||||
Binding the Gitea HTTP server to
0.0.0.0and opening firewall ports makes this dev stack reachable from the network by default. For a local-only on-demand dev stack, prefer binding to127.0.0.1(or make the bind address / openFirewall behavior configurable via module options) to reduce accidental exposure.