{ config, lib, pkgs, ... }: let cfg = config.homelab.apps.coder; postgresUser = "coder"; postgresPassword = "ChangeMe"; postgresDb = "coder"; networkName = "coder"; proxyNet = config.homelab.apps.traefik.sharedNetworkName; coderVersion = "v2.25.3"; coderDbVersion = "17.6"; in { options.homelab.apps.coder = { enable = lib.mkEnableOption "Coder (Docker)"; port = lib.mkOption { type = lib.types.port; default = 7080; description = "Port to expose Coder on."; }; accessUrl = lib.mkOption { type = lib.types.str; description = "The URL to access Coder at."; }; wildcardAccessUrl = lib.mkOption { type = lib.types.str; description = "A wildcard URL to access Coder at (e.g. for workspaces)."; }; db.port = lib.mkOption { type = lib.types.either lib.types.bool lib.types.port; default = false; description = "Port to expose the database on. Set to false to not expose."; }; }; config = lib.mkIf cfg.enable { homelab.virtualisation.containers.enable = true; systemd.services."docker-${networkName}-create-network" = { description = "Create Docker network for ${networkName}"; requiredBy = [ "docker-coder.service" "docker-coderDb.service" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; script = '' if ! ${pkgs.docker}/bin/docker network ls | grep -q ${networkName}; then ${pkgs.docker}/bin/docker network create ${networkName} fi ''; }; virtualisation.oci-containers.containers = { coder = let coderPort = 7080; in { hostname = "coder"; image = "ghcr.io/coder/coder:${coderVersion}"; autoStart = true; dependsOn = [ "coderDb" ]; extraOptions = [ "--group-add" "131" # Add docker group to access the socket # Modify DNS "--dns=192.168.0.91" ]; ports = [ "${toString cfg.port}:${toString coderPort}/tcp" ]; networks = [ networkName proxyNet ]; volumes = [ "/var/run/docker.sock:/var/run/docker.sock" ]; labels = { "traefik.enable" = "true"; "traefik.docker.network" = proxyNet; "traefik.http.routers.coder.rule" = "HostRegexp(`.+\.code\.depeuter\.dev`) || Host(`code.depeuter.dev`)"; "traefik.http.services.coder.loadbalancer.server.port" = toString coderPort; }; environment = { CODER_PG_CONNECTION_URL = "postgresql://${postgresUser}:${postgresPassword}@coder-db/${postgresDb}?sslmode=disable"; # Required if you are not using the tunnel CODER_ACCESS_URL = cfg.accessUrl; CODER_WILDCARD_ACCESS_URL = cfg.wildcardAccessUrl; CODER_DISABLE_PATH_APPS = "false"; # TODO Enable me! CODER_HTTP_ADDRESS = "0.0.0.0:${toString coderPort}"; CODER_TLS_ENABLE = "false"; # TODO Enable me! #CODER_REDIRECT_TO_ACCESS_URL = "true"; # Disable telemetry CODER_TELEMETRY_ENABLED = "false"; }; }; coderDb = { hostname = "coder-db"; image = "postgres:${coderDbVersion}"; autoStart = true; extraOptions = [ ''--health-cmd="pg_isready -U ${postgresUser} -d ${postgresDb}"'' "--health-interval=5s" "--health-timeout=5s" "--health-retries=5" ]; ports = lib.mkIf cfg.db.port [ "${toString cfg.db.port}:5432/tcp" ]; networks = [ networkName ]; volumes = [ "coder_data:/var/lib/postgresql/data" ]; environment = { POSTGRES_USER = postgresUser; POSTGRES_PASSWORD = postgresPassword; POSTGRES_DB = postgresDb; }; }; traefik.cmd = [ "--entrypoints.websecure.http.tls.domains[2].main=code.depeuter.dev" "--entrypoints.websecure.http.tls.domains[2].sans=*.code.depeuter.dev" ]; }; virtualisation.docker.daemon.settings = { dns = [ "192.168.0.91" ]; }; }; }