diff --git a/nix/modules/nixos/features/homelab/default.nix b/nix/modules/nixos/features/homelab/default.nix
index 4058b836..a23216c5 100644
--- a/nix/modules/nixos/features/homelab/default.nix
+++ b/nix/modules/nixos/features/homelab/default.nix
@@ -2,6 +2,7 @@
   imports = [
     ./audiobookshelf.nix
     ./forgejo.nix
+    ./freshrss
     ./gitea.nix
     ./immich.nix
     ./jellyfin.nix
diff --git a/nix/modules/nixos/features/homelab/freshrss/container.nix b/nix/modules/nixos/features/homelab/freshrss/container.nix
new file mode 100644
index 00000000..9576d602
--- /dev/null
+++ b/nix/modules/nixos/features/homelab/freshrss/container.nix
@@ -0,0 +1,88 @@
+# Auto-generated using compose2nix v0.3.2-pre.
+{ pkgs, lib, ... }:
+
+let
+  port = 4003;
+in
+{
+  virtualisation.docker = {
+    enable = true;
+    autoPrune.enable = true;
+  };
+  virtualisation.oci-containers.backend = "docker";
+
+  virtualisation.oci-containers.containers."freshrss" = {
+    image = "lscr.io/linuxserver/freshrss:latest";
+
+    environment = {
+      "PGID" = "1000";
+      "PUID" = "1000";
+      "TZ" = "Etc/UTC";
+    };
+
+    volumes = [
+      "/home/opdavies/Code/dotfiles/freshrss-config:/config:rw"
+    ];
+
+    ports = [
+      "${toString port}:80/tcp"
+    ];
+
+    log-driver = "journald";
+
+    extraOptions = [
+      "--network-alias=freshrss"
+      "--network=freshrss_default"
+    ];
+  };
+
+  systemd.services."docker-freshrss" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "always";
+      RestartMaxDelaySec = lib.mkOverride 90 "1m";
+      RestartSec = lib.mkOverride 90 "100ms";
+      RestartSteps = lib.mkOverride 90 9;
+    };
+
+    after = [
+      "docker-network-freshrss_default.service"
+    ];
+
+    requires = [
+      "docker-network-freshrss_default.service"
+    ];
+
+    partOf = [
+      "docker-compose-freshrss-root.target"
+    ];
+
+    wantedBy = [
+      "docker-compose-freshrss-root.target"
+    ];
+  };
+
+  systemd.services."docker-network-freshrss_default" = {
+    path = [ pkgs.docker ];
+
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+      ExecStop = "docker network rm -f freshrss_default";
+    };
+
+    script = ''
+      docker network inspect freshrss_default || docker network create freshrss_default
+    '';
+
+    partOf = [ "docker-compose-freshrss-root.target" ];
+    wantedBy = [ "docker-compose-freshrss-root.target" ];
+  };
+
+  systemd.targets."docker-compose-freshrss-root" = {
+    unitConfig = {
+      Description = "Root target generated by compose2nix.";
+    };
+
+    wantedBy = [ "multi-user.target" ];
+  };
+}
diff --git a/nix/modules/nixos/features/homelab/freshrss/default.nix b/nix/modules/nixos/features/homelab/freshrss/default.nix
new file mode 100644
index 00000000..f341abe1
--- /dev/null
+++ b/nix/modules/nixos/features/homelab/freshrss/default.nix
@@ -0,0 +1,19 @@
+{ pkgs, lib, ... }:
+
+let
+  port = 4003;
+in
+{
+  imports = [ ./container.nix ];
+
+  services.nginx = {
+    enable = true;
+
+    virtualHosts."freshrss.davies.home".locations."/".proxyPass = "http://localhost:${toString port}/";
+  };
+
+  # TODO: move to pihole.
+  networking.hosts = {
+    "127.0.0.1" = [ "freshrss.davies.home" ];
+  };
+}