diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,13 @@
 The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
 and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
 
+## [1.7.7] - 2025-03-15
+
+### Fixed
+
+- daemon: don't mark successfully retried paths as failed.
+  Addresses failures in CI environments due to intermittent upstream infrastructure issues.
+
 ## [1.7.6] - 2025-01-23
 
 ### Fixed
diff --git a/cachix.cabal b/cachix.cabal
--- a/cachix.cabal
+++ b/cachix.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.2
 name:               cachix
-version:            1.7.6
+version:            1.7.7
 synopsis:
   Command-line client for Nix binary cache hosting https://cachix.org
 
diff --git a/src/Cachix/Daemon/PushManager.hs b/src/Cachix/Daemon/PushManager.hs
--- a/src/Cachix/Daemon/PushManager.hs
+++ b/src/Cachix/Daemon/PushManager.hs
@@ -58,7 +58,7 @@
 import Control.Concurrent.STM.TVar
 import Control.Monad.Catch qualified as E
 import Control.Monad.Trans.Maybe (MaybeT (..), runMaybeT)
-import Control.Retry (RetryStatus)
+import Control.Retry (RetryStatus, rsIterNumber)
 import Data.ByteString qualified as BS
 import Data.HashMap.Strict qualified as HashMap
 import Data.IORef
@@ -338,12 +338,15 @@
         let errText = toS (displayException err)
         sp <- liftIO $ storePathToPath store storePath
         Katip.katipAddContext (Katip.sl "error" errText) $
-          Katip.logFM Katip.InfoS (Katip.ls $ "Failed " <> (toS sp :: Text))
+          Katip.logFM Katip.InfoS (Katip.ls $ "Failed to push " <> (toS sp :: Text))
         pushStorePathFailed (toS sp) errText
 
       onAttempt retryStatus size = do
         sp <- liftIO $ storePathToPath store storePath
-        Katip.logFM Katip.InfoS $ Katip.ls $ "Pushing " <> (toS sp :: Text)
+        Katip.katipAddContext (Katip.sl "retry" (rsIterNumber retryStatus)) $
+          Katip.logFM Katip.InfoS $
+            Katip.ls $
+              "Pushing " <> (toS sp :: Text)
         pushStorePathAttempt (toS sp) size retryStatus
 
       onUncompressedNARStream _ size = do
diff --git a/src/Cachix/Daemon/PushManager/PushJob.hs b/src/Cachix/Daemon/PushManager/PushJob.hs
--- a/src/Cachix/Daemon/PushManager/PushJob.hs
+++ b/src/Cachix/Daemon/PushManager/PushJob.hs
@@ -47,26 +47,22 @@
       pushResult = pushResult {prSkippedPaths = skippedPaths}
     }
 
-addPushedPath :: FilePath -> PushResult -> PushResult
-addPushedPath storePath pushResult =
-  pushResult {prPushedPaths = Set.insert storePath (prPushedPaths pushResult)}
-
-addFailedPath :: FilePath -> PushResult -> PushResult
-addFailedPath storePath pushResult =
-  pushResult {prFailedPaths = Set.insert storePath (prFailedPaths pushResult)}
-
 markStorePathPushed :: FilePath -> PushJob -> PushJob
 markStorePathPushed storePath pushJob@(PushJob {pushQueue, pushResult}) =
   pushJob
     { pushQueue = Set.delete storePath pushQueue,
-      pushResult = addPushedPath storePath pushResult
+      pushResult =
+        pushResult
+          { prPushedPaths = Set.insert storePath (prPushedPaths pushResult),
+            prFailedPaths = Set.delete storePath (prFailedPaths pushResult)
+          }
     }
 
 markStorePathFailed :: FilePath -> PushJob -> PushJob
 markStorePathFailed storePath pushJob@(PushJob {pushQueue, pushResult}) =
   pushJob
     { pushQueue = Set.delete storePath pushQueue,
-      pushResult = addFailedPath storePath pushResult
+      pushResult = pushResult {prFailedPaths = Set.insert storePath (prFailedPaths pushResult)}
     }
 
 status :: PushJob -> JobStatus
diff --git a/test/Daemon/PushManagerSpec.hs b/test/Daemon/PushManagerSpec.hs
--- a/test/Daemon/PushManagerSpec.hs
+++ b/test/Daemon/PushManagerSpec.hs
@@ -88,6 +88,27 @@
               PushJob.prSkippedPaths = mempty
             }
 
+    it "unmark paths as failed after successful retry" $
+      do
+        let request = Protocol.PushRequest {Protocol.storePaths = ["foo", "bar"]}
+            pathSet = Set.fromList ["foo", "bar"]
+            closure = PushJob.ResolvedClosure pathSet pathSet
+
+        timestamp <- getCurrentTime
+        initPushJob <- PushJob.new request
+        let pushJob =
+              initPushJob
+                & PushJob.populateQueue closure timestamp
+                & PushJob.markStorePathFailed "foo"
+                & PushJob.markStorePathPushed "foo"
+        PushJob.status pushJob `shouldBe` Running
+        PushJob.result pushJob
+          `shouldBe` PushJob.PushResult
+            { PushJob.prFailedPaths = mempty,
+              PushJob.prPushedPaths = Set.fromList ["foo"],
+              PushJob.prSkippedPaths = mempty
+            }
+
   describe "push manager" $ do
     it "queues push jobs " $ inPushManager $ do
       let request = Protocol.PushRequest {Protocol.storePaths = ["foo", "bar"]}
