diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index f76ae66..8a08a49 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -9,6 +9,7 @@ on:
     - master
     - release/*
     - test-please/*
+    - use-atomic-set
   pull_request:
     paths-ignore:
     # ignore top-level markdown files (CHANGELOG.md, README.md, etc.)
diff --git a/README.md b/README.md
index 8d193c5..9af9b67 100644
--- a/README.md
+++ b/README.md
@@ -694,7 +694,7 @@ storage_config = {
 }
 ```
 
-Redis >= 2.6.0 is required as this storage requires [PEXPIRE](https://redis.io/commands/pexpire).
+Redis >= 2.6.12 is required as this storage requires [SET EX](https://redis.io/commands/set).
 
 ### vault
 
diff --git a/lib/resty/acme/storage/redis.lua b/lib/resty/acme/storage/redis.lua
index c9daf2b..9d5736f 100644
--- a/lib/resty/acme/storage/redis.lua
+++ b/lib/resty/acme/storage/redis.lua
@@ -95,35 +95,32 @@ local function remove_namespace(namespace, keys)
   end
 end
 
--- TODO: use EX/NX flag if we can determine redis version (>=2.6.12)
 function _M:add(k, v, ttl)
   k = self.namespace .. k
-  local ok, err = op(self, 'setnx', k, v)
+  local ok, err
+  if ttl then
+    ok, err = op(self, 'set', k, v, "nx", "px", math.floor(ttl * 1000))
+  else
+    ok, err = op(self, 'set', k, v, "nx")
+  end
   if err then
     return err
-  elseif ok == 0 then
+  elseif ok == ngx.null then
     return "exists"
   end
-  if ttl then
-    local _, err = op(self, 'pexpire', k, math.floor(ttl * 1000))
-    if err then
-      return err
-    end
-  end
 end
 
 function _M:set(k, v, ttl)
   k = self.namespace .. k
-  local _, err = op(self, 'set', k, v)
+  local err, _
+  if ttl then
+    _, err = op(self, 'set', k, v, "px", math.floor(ttl * 1000))
+  else
+    _, err = op(self, 'set', k, v)
+  end
   if err then
     return err
   end
-  if ttl then
-    local _, err = op(self, 'pexpire', k, math.floor(ttl * 1000))
-    if err then
-      return err
-    end
-  end
 end
 
 function _M:delete(k)