diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml
index 64e230c..0ed2cbd 100644
--- a/.github/workflows/build-ubuntu.yml
+++ b/.github/workflows/build-ubuntu.yml
@@ -19,6 +19,7 @@ jobs:
   build-tests:
     runs-on: ${{matrix.os}}
     strategy:
+      fail-fast: false
       matrix:
         os: [ubuntu-latest, ubuntu-24.04, ubuntu-22.04, ubuntu-20.04, macos-latest, macos-14, macos-13]
         rmq: [WITH_RMQ=ON, WITH_RMQ=OFF]
diff --git a/src/rest_server.cpp b/src/rest_server.cpp
index 820fa50..a23d12f 100644
--- a/src/rest_server.cpp
+++ b/src/rest_server.cpp
@@ -377,7 +377,7 @@ namespace lws
             {
               std::shared_ptr<frame> self_;
 
-              void operator()(boost::system::error_code error) const
+              void operator()(const boost::system::error_code error) const
               {
                 if (!self_ || error == boost::asio::error::operation_aborted)
                   return;
@@ -385,7 +385,7 @@ namespace lws
                 assert(self_->strand.running_in_this_thread());
                 MWARNING("Timeout on /daemon_status ZMQ call");
                 self_->client.close = true;
-                self_->client.asock->cancel(error);
+                self_->client.asock->cancel();
               }
             };
 
@@ -420,7 +420,7 @@ namespace lws
                 self.client, self.in, boost::asio::bind_executor(self.strand, std::move(*this))
               );
 
-              if (!self.timer.cancel(error))
+              if (!self.timer.cancel())
                 return send_response(boost::asio::error::operation_aborted, json_response(async_response{}));
 
               {
@@ -838,7 +838,7 @@ namespace lws
                 net::zmq::async_read(self_->client, in, yield[error]);
                 if (error)
                   return send_response(error, async_ready());
-                if (!self_->timer.cancel(error))
+                if (!self_->timer.cancel())
                   return send_response(boost::asio::error::operation_aborted, async_ready());
 
                 histogram_rpc::Response histogram_resp{};
@@ -884,7 +884,7 @@ namespace lws
                 net::zmq::async_read(self_->client, in, yield[error]);
                 if (error)
                   return send_response(error, async_ready());
-                if (!self_->timer.cancel(error))
+                if (!self_->timer.cancel())
                   return send_response(boost::asio::error::operation_aborted, async_ready());
 
                 distribution_rpc::Response distribution_resp{};
@@ -952,7 +952,7 @@ namespace lws
                     MERROR("Internal ZMQ error in /get_random_outs: " << error.message());
                     return {error::daemon_timeout};
                   }
-                  if (!self_.self_->timer.cancel(error))
+                  if (!self_.self_->timer.cancel())
                     return {error::daemon_timeout};
 
                   get_keys_rpc::Response keys_resp{};
@@ -1233,7 +1233,7 @@ namespace lws
                 self.client, self.in, boost::asio::bind_executor(self.strand, std::move(*this))
               );
 
-              if (!self.timer.cancel(error))
+              if (!self.timer.cancel())
                 return send_response(boost::asio::error::operation_aborted, default_response{});
 
               {
@@ -1579,7 +1579,7 @@ namespace lws
                   self.client, self.in, boost::asio::bind_executor(self.strand, std::move(*this))
                 );
 
-                if (!self.timer.cancel(error))
+                if (!self.timer.cancel())
                   return send_response(boost::asio::error::operation_aborted, async_ready());
 
                 {
@@ -2021,7 +2021,7 @@ namespace lws
       boost::system::error_code ec{};
       MDEBUG("Shutting down REST socket to " << this);
       sock().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
-      timer_.cancel(ec);
+      timer_.cancel();
     }
   };
 
@@ -2150,7 +2150,7 @@ namespace lws
           );
 
           // async_response will have its own timeouts set in handlers if async
-          if (!self.timer_.cancel(error))
+          if (!self.timer_.cancel())
             return self.shutdown();
 
           /* async_response flow has MDEBUG statements for outgoing messages.
@@ -2249,8 +2249,7 @@ namespace lws
       if (!https)
       {
         boost::system::error_code error{};
-        const boost::asio::ip::address ip_host =
-          ip_host.from_string(url.host, error);
+        const auto ip_host = boost::asio::ip::make_address(url.host, error);
         if (error)
           MONERO_THROW(lws::error::configuration, "Invalid IP address for REST server");
         if (!ip_host.is_loopback() && !config.allow_external)
@@ -2310,7 +2309,7 @@ namespace lws
       ssl_options.auth = std::move(config.auth);
 
       boost::asio::ip::tcp::endpoint endpoint{
-        boost::asio::ip::address::from_string(url.host),
+        boost::asio::ip::make_address(url.host),
         boost::lexical_cast<unsigned short>(url.port)
       };
 
diff --git a/src/rpc/scanner/client.cpp b/src/rpc/scanner/client.cpp
index a4837fd..2539508 100644
--- a/src/rpc/scanner/client.cpp
+++ b/src/rpc/scanner/client.cpp
@@ -121,7 +121,7 @@ namespace lws { namespace rpc { namespace scanner
         for (;;)
         {
           MINFO("Attempting connection to " << self_->server_address_);
-          self_->connect_timer_.expires_from_now(connect_timeout);
+          self_->connect_timer_.expires_after(connect_timeout);
           self_->connect_timer_.async_wait(
             boost::asio::bind_executor(self_->strand_, close{self_})
           );
@@ -139,7 +139,7 @@ namespace lws { namespace rpc { namespace scanner
             break;
 
           MINFO("Retrying connection in " << std::chrono::seconds{reconnect_interval}.count() << " seconds"); 
-          self_->connect_timer_.expires_from_now(reconnect_interval);
+          self_->connect_timer_.expires_after(reconnect_interval);
           BOOST_ASIO_CORO_YIELD self_->connect_timer_.async_wait(
             boost::asio::bind_executor(self_->strand_, *this)
           );
diff --git a/src/rpc/scanner/server.cpp b/src/rpc/scanner/server.cpp
index 22deb12..6672c65 100644
--- a/src/rpc/scanner/server.cpp
+++ b/src/rpc/scanner/server.cpp
@@ -203,7 +203,7 @@ namespace lws { namespace rpc { namespace scanner
         return;
 
       assert(self_->strand_.running_in_this_thread());
-      self_->check_timer_.expires_from_now(account_poll_interval);
+      self_->check_timer_.expires_after(account_poll_interval);
       self_->check_timer_.async_wait(boost::asio::bind_executor(self_->strand_, *this));
 
       std::size_t total_threads = self_->local_.size();
@@ -422,7 +422,7 @@ namespace lws { namespace rpc { namespace scanner
 
     MDEBUG("Stopping rpc::scanner::server async operations");
     boost::system::error_code error{};
-    check_timer_.cancel(error);
+    check_timer_.cancel();
     acceptor_.cancel(error);
     acceptor_.close(error);
 
@@ -454,7 +454,7 @@ namespace lws { namespace rpc { namespace scanner
       }
     }
     return boost::asio::ip::tcp::endpoint{
-      boost::asio::ip::address::from_string(host), boost::lexical_cast<unsigned short>(port)
+      boost::asio::ip::make_address(host), boost::lexical_cast<unsigned short>(port)
     };
   }
 
diff --git a/src/rpc/scanner/write_commands.h b/src/rpc/scanner/write_commands.h
index 34d8075..b91b931 100644
--- a/src/rpc/scanner/write_commands.h
+++ b/src/rpc/scanner/write_commands.h
@@ -118,7 +118,7 @@ namespace lws { namespace rpc { namespace scanner
       {
         while (!self_->write_bufs_.empty())
         {
-          self_->write_timeout_.expires_from_now(std::chrono::seconds{10});
+          self_->write_timeout_.expires_after(std::chrono::seconds{10});
           self_->write_timeout_.async_wait(boost::asio::bind_executor(self_->strand_, timeout<T>{self_}));
           BOOST_ASIO_CORO_YIELD boost::asio::async_write(
             self_->sock_, self_->write_buffer(), boost::asio::bind_executor(self_->strand_, *this)
@@ -190,6 +190,17 @@ namespace lws { namespace rpc { namespace scanner
         : self_(rhs.self_), msg_(rhs.msg_.clone())
       {}
 
+      queue_slice& operator=(queue_slice&&) = default;
+      queue_slice& operator=(const queue_slice& rhs)
+      {
+        if (this != std::addressof(rhs))
+        {
+          self_ = rhs.self_;
+          msg_ = rhs.msg_.clone();
+        }
+        return *this;
+      }
+
       void operator()()
       {
         if (!self_)
diff --git a/src/scanner.cpp b/src/scanner.cpp
index 3ffc7de..a976fa2 100644
--- a/src/scanner.cpp
+++ b/src/scanner.cpp
@@ -1337,7 +1337,7 @@ namespace lws
         const expect<void> status = ctx_.retrieve_rates_async(io_);
         if (!status)
           MERROR("Unable to retrieve exchange rates: " << status.error());
-        rate_timer_.expires_from_now(rate_interval_);
+        rate_timer_.expires_after(rate_interval_);
         rate_timer_.async_wait(*this);
       }
 
@@ -1382,7 +1382,7 @@ namespace lws
         MINFO("No active accounts");
 
         boost::asio::steady_timer poll{sync_.io_};
-        poll.expires_from_now(rpc::scanner::account_poll_interval);
+        poll.expires_after(rpc::scanner::account_poll_interval);
         const auto ready = poll.async_wait(boost::asio::use_future);
 
         /* The exchange rates timer could run while waiting, so ensure that