diff --git a/Gemfile b/Gemfile
index 9917ae2379d8d91346b5061be2ba83d10aed7158..dfded7b4634046d6531cd18213485926d9efa269 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,5 +1,5 @@
 source 'https://rubygems.org'
-ruby '2.7.6'
+ruby '>= 2.7', '< 4'
 
 # Essential gems: servers, adapters, Rails + Rails requirements
 gem 'coffee-rails', '~> 5.0.0'
@@ -62,6 +62,9 @@ gem 'stackprof', '~> 0.2'
 gem 'e2mmap', '~> 0.1'
 gem 'thwait', '~> 0.2'
 
+# Ruby 3.0 compatibility: net-smtp is no longer bundled with Ruby
+gem 'net-smtp', '~> 0.3'
+
 # Stuff for imports
 gem 'ruby-progressbar', '~> 1.11'
 
diff --git a/Gemfile.lock b/Gemfile.lock
index 74778fee98e1af4a6152f3d3e8ee6c0c00bba7b6..98cc61909b9125dbed69ba5d9c44682bf6088cc1 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,67 +1,67 @@
 GEM
   remote: https://rubygems.org/
   specs:
-    actioncable (7.0.3.1)
-      actionpack (= 7.0.3.1)
-      activesupport (= 7.0.3.1)
+    actioncable (7.0.4)
+      actionpack (= 7.0.4)
+      activesupport (= 7.0.4)
       nio4r (~> 2.0)
       websocket-driver (>= 0.6.1)
-    actionmailbox (7.0.3.1)
-      actionpack (= 7.0.3.1)
-      activejob (= 7.0.3.1)
-      activerecord (= 7.0.3.1)
-      activestorage (= 7.0.3.1)
-      activesupport (= 7.0.3.1)
+    actionmailbox (7.0.4)
+      actionpack (= 7.0.4)
+      activejob (= 7.0.4)
+      activerecord (= 7.0.4)
+      activestorage (= 7.0.4)
+      activesupport (= 7.0.4)
       mail (>= 2.7.1)
       net-imap
       net-pop
       net-smtp
-    actionmailer (7.0.3.1)
-      actionpack (= 7.0.3.1)
-      actionview (= 7.0.3.1)
-      activejob (= 7.0.3.1)
-      activesupport (= 7.0.3.1)
+    actionmailer (7.0.4)
+      actionpack (= 7.0.4)
+      actionview (= 7.0.4)
+      activejob (= 7.0.4)
+      activesupport (= 7.0.4)
       mail (~> 2.5, >= 2.5.4)
       net-imap
       net-pop
       net-smtp
       rails-dom-testing (~> 2.0)
-    actionpack (7.0.3.1)
-      actionview (= 7.0.3.1)
-      activesupport (= 7.0.3.1)
+    actionpack (7.0.4)
+      actionview (= 7.0.4)
+      activesupport (= 7.0.4)
       rack (~> 2.0, >= 2.2.0)
       rack-test (>= 0.6.3)
       rails-dom-testing (~> 2.0)
       rails-html-sanitizer (~> 1.0, >= 1.2.0)
-    actiontext (7.0.3.1)
-      actionpack (= 7.0.3.1)
-      activerecord (= 7.0.3.1)
-      activestorage (= 7.0.3.1)
-      activesupport (= 7.0.3.1)
+    actiontext (7.0.4)
+      actionpack (= 7.0.4)
+      activerecord (= 7.0.4)
+      activestorage (= 7.0.4)
+      activesupport (= 7.0.4)
       globalid (>= 0.6.0)
       nokogiri (>= 1.8.5)
-    actionview (7.0.3.1)
-      activesupport (= 7.0.3.1)
+    actionview (7.0.4)
+      activesupport (= 7.0.4)
       builder (~> 3.1)
       erubi (~> 1.4)
       rails-dom-testing (~> 2.0)
       rails-html-sanitizer (~> 1.1, >= 1.2.0)
-    activejob (7.0.3.1)
-      activesupport (= 7.0.3.1)
+    activejob (7.0.4)
+      activesupport (= 7.0.4)
       globalid (>= 0.3.6)
-    activemodel (7.0.3.1)
-      activesupport (= 7.0.3.1)
-    activerecord (7.0.3.1)
-      activemodel (= 7.0.3.1)
-      activesupport (= 7.0.3.1)
-    activestorage (7.0.3.1)
-      actionpack (= 7.0.3.1)
-      activejob (= 7.0.3.1)
-      activerecord (= 7.0.3.1)
-      activesupport (= 7.0.3.1)
+    activemodel (7.0.4)
+      activesupport (= 7.0.4)
+    activerecord (7.0.4)
+      activemodel (= 7.0.4)
+      activesupport (= 7.0.4)
+    activestorage (7.0.4)
+      actionpack (= 7.0.4)
+      activejob (= 7.0.4)
+      activerecord (= 7.0.4)
+      activesupport (= 7.0.4)
       marcel (~> 1.0)
       mini_mime (>= 1.1.0)
-    activesupport (7.0.3.1)
+    activesupport (7.0.4)
       concurrent-ruby (~> 1.0, >= 1.0.2)
       i18n (>= 1.6, < 2)
       minitest (>= 5.1)
@@ -71,8 +71,8 @@ GEM
     ast (2.4.2)
     awesome_print (1.9.2)
     aws-eventstream (1.2.0)
-    aws-partitions (1.626.0)
-    aws-sdk-core (3.140.0)
+    aws-partitions (1.628.0)
+    aws-sdk-core (3.145.0)
       aws-eventstream (~> 1, >= 1.0.2)
       aws-partitions (~> 1, >= 1.525.0)
       aws-sigv4 (~> 1.1)
@@ -224,20 +224,20 @@ GEM
       rack (>= 1.2.0)
     rack-test (2.0.2)
       rack (>= 1.3)
-    rails (7.0.3.1)
-      actioncable (= 7.0.3.1)
-      actionmailbox (= 7.0.3.1)
-      actionmailer (= 7.0.3.1)
-      actionpack (= 7.0.3.1)
-      actiontext (= 7.0.3.1)
-      actionview (= 7.0.3.1)
-      activejob (= 7.0.3.1)
-      activemodel (= 7.0.3.1)
-      activerecord (= 7.0.3.1)
-      activestorage (= 7.0.3.1)
-      activesupport (= 7.0.3.1)
+    rails (7.0.4)
+      actioncable (= 7.0.4)
+      actionmailbox (= 7.0.4)
+      actionmailer (= 7.0.4)
+      actionpack (= 7.0.4)
+      actiontext (= 7.0.4)
+      actionview (= 7.0.4)
+      activejob (= 7.0.4)
+      activemodel (= 7.0.4)
+      activerecord (= 7.0.4)
+      activestorage (= 7.0.4)
+      activesupport (= 7.0.4)
       bundler (>= 1.15.0)
-      railties (= 7.0.3.1)
+      railties (= 7.0.4)
     rails-controller-testing (1.0.5)
       actionpack (>= 5.0.1.rc1)
       actionview (>= 5.0.1.rc1)
@@ -247,9 +247,9 @@ GEM
       nokogiri (>= 1.6)
     rails-html-sanitizer (1.4.3)
       loofah (~> 2.3)
-    railties (7.0.3.1)
-      actionpack (= 7.0.3.1)
-      activesupport (= 7.0.3.1)
+    railties (7.0.4)
+      actionpack (= 7.0.4)
+      activesupport (= 7.0.4)
       method_source
       rake (>= 12.2)
       thor (~> 1.0)
@@ -259,9 +259,9 @@ GEM
     rb-fsevent (0.11.2)
     rb-inotify (0.10.1)
       ffi (~> 1.0)
-    redis (5.0.2)
-      redis-client (~> 0.7)
-    redis-client (0.7.1)
+    redis (5.0.4)
+      redis-client (>= 0.7.4)
+    redis-client (0.8.0)
       connection_pool
     regexp_parser (2.5.0)
     responders (3.0.1)
@@ -288,10 +288,10 @@ GEM
       unicode-display_width (>= 1.4.0, < 3.0)
     rubocop-ast (1.21.0)
       parser (>= 3.1.1.0)
-    rubocop-rails (2.15.2)
+    rubocop-rails (2.16.0)
       activesupport (>= 4.2.0)
       rack (>= 1.1)
-      rubocop (>= 1.7.0, < 2.0)
+      rubocop (>= 1.33.0, < 2.0)
     ruby-progressbar (1.11.0)
     ruby-vips (2.1.4)
       ffi (~> 1.12)
@@ -358,7 +358,6 @@ GEM
     zeitwerk (2.6.0)
 
 PLATFORMS
-  ruby
   x86_64-linux
 
 DEPENDENCIES
@@ -386,6 +385,7 @@ DEPENDENCIES
   minitest (~> 5.16.0)
   minitest-ci (~> 3.4.0)
   mysql2 (~> 0.5.4)
+  net-smtp (~> 0.3)
   omniauth (~> 1.9)
   premailer-rails (~> 1.11)
   puma (~> 5.6)
diff --git a/app/models/request_context.rb b/app/models/request_context.rb
index 6967f1167b09269f53c263975ac3d8c294fd4ac2..eea9e0c56647f5919fcb66b8b9ba874feea94074 100644
--- a/app/models/request_context.rb
+++ b/app/models/request_context.rb
@@ -13,7 +13,12 @@ class RequestContext
         $redis
       else
         processed = ERB.new(File.read(Rails.root.join('config', 'database.yml'))).result(binding)
-        $redis ||= Redis.new(YAML.safe_load(processed, [], [], true)["redis_#{Rails.env}"].deep_symbolize_keys)
+        $redis ||= Redis.new(
+          YAML.safe_load(processed,
+                         permitted_classes: [],
+                         permitted_symbols: [],
+                         aliases: true)["redis_#{Rails.env}"].deep_symbolize_keys
+        )
       end
     rescue NoMethodError
       raise LoadError, "You don't appear to have any Redis config in config/database.yml"
diff --git a/config/environments/development.rb b/config/environments/development.rb
index bea4950c288fd38c9809a985baf83ce313eea659..06ab403381b00391f412c48da1e5c0f9b07a43f5 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -21,7 +21,7 @@ Rails.application.configure do
 
   # Set the cache store to the redis that was configured in the database.yml
   processed = ERB.new(File.read(Rails.root.join('config', 'database.yml'))).result(binding)
-  redis_config = YAML.safe_load(processed, [], [], true)["redis_#{Rails.env}"]
+  redis_config = YAML.safe_load(processed, permitted_classes: [], permitted_symbols: [], aliases: true)["redis_#{Rails.env}"]
   config.cache_store = QPixel::NamespacedEnvCache.new(
     ActiveSupport::Cache::RedisCacheStore.new(
       url: "redis://#{redis_config['host']}:#{redis_config['port']}"
diff --git a/config/environments/test.rb b/config/environments/test.rb
index 1b6bc80c8f81c8c77247bfba03bc40213d4af1a6..4c5ef5309711a0e4c6fb2a5213cebb2c07ac63b9 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -29,7 +29,7 @@ Rails.application.configure do
   config.action_controller.perform_caching = false
 
   processed = ERB.new(File.read(Rails.root.join('config', 'database.yml'))).result(binding)
-  redis_config = YAML.safe_load(processed, [], [], true)["redis_#{Rails.env}"]
+  redis_config = YAML.safe_load(processed, permitted_classes: [], permitted_symbols: [], aliases: true)["redis_#{Rails.env}"]
   config.cache_store = QPixel::NamespacedEnvCache.new(
     ActiveSupport::Cache::RedisCacheStore.new(
       url: "redis://#{redis_config['host']}:#{redis_config['port']}"