{
  config,
  pkgs,
  lib,
  ...
}: let
  giteaSecretFile = config.sops.secrets."woodpecker/gitea".path;
  sharedSecretFile = config.sops.secrets."woodpecker/shared".path;

  port = 3030;
  rpcPort = 3031;
in {
  sops.secrets."woodpecker/gitea" = {};
  sops.secrets."woodpecker/shared" = {};

  services.postgresql = {
    enable = true;

    ensureDatabases = ["woodpecker"];

    ensureUsers = [
      {
        name = "woodpecker";
        ensurePermissions = {
          "DATABASE woodpecker" = "ALL PRIVILEGES";
        };
      }
    ];
  };

  services.nginx.virtualHosts."woodpecker.defenestrated.systems" = {
    enableACME = true;
    forceSSL = true;
    locations."/".proxyPass = "http://localhost:${builtins.toString port}/";
  };

  services = {
    woodpecker-server = {
      enable = true;

      environment = {
        WOODPECKER_HOST = "https://woodpecker.defenestrated.systems";
        WOODPECKER_OPEN = "false";
        WOODPECKER_GITEA = "true";
        WOODPECKER_GITEA_CLIENT = "true";
        WOODPECKER_GITEA_URL = config.services.gitea.settings.server.ROOT_URL;
        WOODPECKER_ADMIN = "lukaswrz";

        WOODPECKER_DATABASE_DRIVER = "postgres";
        WOODPECKER_DATABASE_DATASOURCE = "postgres:///woodpecker?host=/run/postgresql";

        WOODPECKER_SERVER_ADDR = ":${builtins.toString port}";
        WOODPECKER_GRPC_ADDR = ":${builtins.toString rpcPort}";

        WOODPECKER_LOG_LEVEL = "debug";
      };
    };

    woodpecker-agents = {
      agents.exec = {
        enable = true;

        environment = {
          WOODPECKER_SERVER = "localhost:${toString rpcPort}";
          WOODPECKER_MAX_WORKFLOWS = "10";
          WOODPECKER_BACKEND = "local";
          WOODPECKER_FILTER_LABELS = "type=exec";
          WOODPECKER_HEALTHCHECK = "false";

          NIX_REMOTE = "daemon";
          PAGER = "cat";
        };

        environmentFile = [sharedSecretFile];
      };

      agents.docker = {
        enable = true;

        environment = {
          WOODPECKER_SERVER = "localhost:${toString rpcPort}";
          WOODPECKER_MAX_WORKFLOWS = "10";
          WOODPECKER_BACKEND = "docker";
          WOODPECKER_FILTER_LABELS = "type=docker";
          WOODPECKER_HEALTHCHECK = "false";
        };

        environmentFile = [sharedSecretFile];

        extraGroups = ["docker"];
      };
    };
  };

  systemd.services.woodpecker-server = {
    serviceConfig = {
      # Set username for database access
      User = "woodpecker";

      BindPaths = [
        # Allow access to the database path
        "/run/postgresql"
      ];

      # Why is services.woodpecker-server.environmentFile just a single path?
      EnvironmentFile = [giteaSecretFile sharedSecretFile];
    };
  };

  # Adjust exec runner service for Nix
  systemd.services.woodpecker-agent-exec = {
    # Might break deployment
    restartIfChanged = false;

    path = with pkgs; [
      woodpecker-plugin-git
      bash
      coreutils
      git
      git-lfs
      gnutar
      gzip
      nix
    ];

    serviceConfig = {
      # Same option as upstream, without @setuid
      SystemCallFilter = lib.mkForce "~@clock @privileged @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @swap";

      BindPaths = [
        "/nix/var/nix/daemon-socket/socket"
        "/run/nscd/socket"
      ];

      BindReadOnlyPaths = [
        "/etc/passwd:/etc/passwd"
        "/etc/group:/etc/group"
        "/etc/nix:/etc/nix"
        "${config.environment.etc."ssh/ssh_known_hosts".source}:/etc/ssh/ssh_known_hosts"
        "/etc/machine-id"
        # Channels are dynamic paths in the nix store, therefore we need to bind mount the whole thing
        "/nix/"
      ];
    };
  };

  virtualisation.docker.enable = true;

  # Adjust Docker runner service for Nix
  systemd.services.woodpecker-agent-docker = {
    after = ["docker.socket"];

    # Might break deployment
    restartIfChanged = false;

    serviceConfig = {
      BindPaths = [
        "/var/run/docker.sock"
      ];
    };
  };
}