diff --git a/nix-cache-server.cabal b/nix-cache-server.cabal
new file mode 100644
--- /dev/null
+++ b/nix-cache-server.cabal
@@ -0,0 +1,66 @@
+cabal-version: 2.0
+
+-- This file has been generated from package.yaml by hpack version 0.38.2.
+--
+-- see: https://github.com/sol/hpack
+
+name:           nix-cache-server
+version:        0.1.0.0
+synopsis:       Nix binary cache server using nix-serve-ng
+description:    WAI application for serving a Nix binary cache using nix-serve-ng
+category:       System
+homepage:       https://github.com/juspay/vira/tree/main/packages/nix-cache-server
+author:         Sridhar Ratnakumar
+maintainer:     srid@srid.ca
+copyright:      2025 Sridhar Ratnakumar
+license:        MIT
+build-type:     Simple
+
+library
+  exposed-modules:
+      System.Nix.Cache.Server
+  other-modules:
+      Paths_nix_cache_server
+  autogen-modules:
+      Paths_nix_cache_server
+  hs-source-dirs:
+      src
+  default-extensions:
+      DataKinds
+      DeriveDataTypeable
+      DeriveGeneric
+      DeriveTraversable
+      DerivingStrategies
+      DerivingVia
+      ExplicitForAll
+      FlexibleContexts
+      FlexibleInstances
+      GeneralizedNewtypeDeriving
+      ImportQualifiedPost
+      LambdaCase
+      MultiParamTypeClasses
+      MultiWayIf
+      NamedFieldPuns
+      NoStarIsType
+      NumericUnderscores
+      OverloadedStrings
+      ScopedTypeVariables
+      StrictData
+      TypeApplications
+      TypeFamilies
+      TypeOperators
+      TypeSynonymInstances
+      ViewPatterns
+  ghc-options: -Wall -optP-Wno-nonportable-include-path -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-deriving-strategies -Wunused-foralls -fprint-explicit-foralls -fprint-explicit-kinds
+  build-depends:
+      base ==4.*
+    , bytestring
+    , nix
+    , nix-serve-ng
+    , relude >=1.0
+    , wai
+  mixins:
+      base hiding (Prelude)
+    , relude (Relude as Prelude, Relude.Container.One)
+    , relude 
+  default-language: GHC2021
diff --git a/src/System/Nix/Cache/Server.hs b/src/System/Nix/Cache/Server.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Nix/Cache/Server.hs
@@ -0,0 +1,108 @@
+{-# LANGUAGE DuplicateRecordFields #-}
+
+{- | Nix binary cache server integration using nix-serve-ng
+
+This module provides a WAI application for serving a Nix binary cache using
+the <https://github.com/aristanetworks/nix-serve-ng nix-serve-ng> library.
+
+= Usage
+
+The typical flow is:
+
+1. Generate or load cache signing keys using "System.Nix.Cache.Keys"
+2. Call 'makeCacheApplication' with the keys to create a WAI Application
+3. Mount the application in your web server (e.g., at @\/cache@)
+
+The resulting cache server exposes standard Nix cache endpoints like:
+
+* @\/nix-cache-info@ - Cache metadata
+* @\/*.narinfo@ - NAR info files
+* @\/nar\/*.nar@ - NAR archives
+
+This integrates with nix-serve-ng which handles all the Nix-specific
+protocols and signing of NAR files.
+-}
+module System.Nix.Cache.Server (
+  -- * Cache server
+  makeCacheServer,
+  cacheMiddleware,
+) where
+
+import Data.ByteString qualified as ByteString
+import Network.Wai (Application, Middleware, pathInfo, rawPathInfo)
+import Nix qualified
+import NixServeNg (ApplicationOptions (..), makeApplication)
+import System.Nix.Cache.Keys (SecretKey, secretKeyByteString)
+
+{- | Create a cache server WAI Application
+
+This function:
+
+1. Initializes the Nix store using the @nix@ library
+2. Retrieves the Nix store directory path
+3. Creates a nix-serve-ng application with the provided signing key
+
+The returned WAI 'Application' can be mounted at any path in your web server
+(commonly @\/cache@). It will handle all Nix binary cache protocol requests.
+
+The cache is configured with a hardcoded priority of 30 (lower numbers have
+higher priority in Nix's cache resolution).
+
+Example:
+
+@
+CacheKeys {secretKey, publicKey} <- ensureCacheKeys "\/path\/to\/keys"
+cacheApp <- makeCacheServer secretKey
+-- Mount cacheApp at \/cache in your WAI server
+-- Use publicKey for UI display
+@
+-}
+makeCacheServer :: SecretKey -> IO Application
+makeCacheServer secretKey = do
+  -- Initialize Nix store
+  Nix.initStore
+
+  -- Get store directory
+  storePath <- Nix.getStoreDir
+
+  -- Create application options with hardcoded priority
+  let options =
+        ApplicationOptions
+          { priority = 30 -- Lower is higher priority
+          , storeDirectory = storePath
+          , secretKey = Just (secretKeyByteString secretKey)
+          }
+
+  -- Return the application
+  pure $ makeApplication options
+
+{- | Middleware to mount cache server at a path prefix
+
+This middleware intercepts requests starting with the given path prefix
+(e.g., @\/cache\/*@) and routes them to the cache application, while allowing
+requests to the prefix itself (e.g., @\/cache@) to pass through to the main
+application.
+
+This is useful when you want to serve a UI page at @\/cache@ while serving
+the actual cache protocol at @\/cache\/nix-cache-info@, @\/cache\/*.narinfo@, etc.
+
+Example:
+
+@
+cacheApp <- makeCacheServer secretKey
+let middleware = cacheMiddleware "cache" cacheApp
+@
+-}
+cacheMiddleware :: Text -> Application -> Middleware
+cacheMiddleware prefix cacheApp app req respond =
+  case pathInfo req of
+    (p : rest@(_ : _)) | p == prefix -> do
+      -- Only intercept if there's at least one path segment after prefix
+      -- This allows /prefix (UI page) to pass through to servant
+      -- while /prefix/nix-cache-info, /prefix/*.narinfo, etc go to cache app
+      let rawPath = rawPathInfo req
+          prefixBytes = "/" <> encodeUtf8 prefix
+          rawPath' = fromMaybe rawPath $ ByteString.stripPrefix prefixBytes rawPath
+          req' = req {pathInfo = rest, rawPathInfo = rawPath'}
+      cacheApp req' respond
+    _ -> app req respond
