diff --git a/src/dedicated_room/citra-room.cpp b/src/dedicated_room/citra-room.cpp
index 9246b5797..8e2938006 100644
--- a/src/dedicated_room/citra-room.cpp
+++ b/src/dedicated_room/citra-room.cpp
@@ -3,6 +3,7 @@
 // Refer to the license.txt file included.
 
 #include <chrono>
+#include <fstream>
 #include <iostream>
 #include <memory>
 #include <regex>
@@ -27,10 +28,12 @@
 #include "common/common_types.h"
 #include "common/detached_tasks.h"
 #include "common/scm_rev.h"
+#include "common/string_util.h"
 #include "core/announce_multiplayer_session.h"
 #include "core/core.h"
 #include "core/settings.h"
 #include "network/network.h"
+#include "network/room.h"
 #include "network/verify_user.h"
 
 #ifdef ENABLE_WEB_SERVICE
@@ -50,6 +53,7 @@ static void PrintHelp(const char* argv0) {
                  "--username          The username used for announce\n"
                  "--token             The token used for announce\n"
                  "--web-api-url       Citra Web API url\n"
+                 "--ban-list-file     The file for storing the room ban list\n"
                  "-h, --help          Display this help and exit\n"
                  "-v, --version       Output version information and exit\n";
 }
@@ -59,6 +63,71 @@ static void PrintVersion() {
               << " Libnetwork: " << Network::network_version << std::endl;
 }
 
+/// The magic text at the beginning of a citra-room ban list file.
+static constexpr char BanListMagic[] = "CitraRoom-BanList-1";
+
+static Network::Room::BanList LoadBanList(const std::string& path) {
+    std::ifstream file;
+    OpenFStream(file, path, std::ios_base::in);
+    if (!file || file.eof()) {
+        std::cout << "Could not open ban list!\n\n";
+        return {};
+    }
+    std::string magic;
+    std::getline(file, magic);
+    if (magic != BanListMagic) {
+        std::cout << "Ban list is not valid!\n\n";
+        return {};
+    }
+
+    // false = username ban list, true = ip ban list
+    bool ban_list_type = false;
+    Network::Room::UsernameBanList username_ban_list;
+    Network::Room::IPBanList ip_ban_list;
+    while (!file.eof()) {
+        std::string line;
+        std::getline(file, line);
+        line.erase(std::remove(line.begin(), line.end(), '\0'), line.end());
+        line = Common::StripSpaces(line);
+        if (line.empty()) {
+            // An empty line marks start of the IP ban list
+            ban_list_type = true;
+            continue;
+        }
+        if (ban_list_type) {
+            ip_ban_list.emplace_back(line);
+        } else {
+            username_ban_list.emplace_back(line);
+        }
+    }
+
+    return {username_ban_list, ip_ban_list};
+}
+
+static void SaveBanList(const Network::Room::BanList& ban_list, const std::string& path) {
+    std::ofstream file;
+    OpenFStream(file, path, std::ios_base::out);
+    if (!file) {
+        std::cout << "Could not save ban list!\n\n";
+        return;
+    }
+
+    file << BanListMagic << "\n";
+
+    // Username ban list
+    for (const auto& username : ban_list.first) {
+        file << username << "\n";
+    }
+    file << "\n";
+
+    // IP ban list
+    for (const auto& ip : ban_list.second) {
+        file << ip << "\n";
+    }
+
+    file.flush();
+}
+
 /// Application entry point
 int main(int argc, char** argv) {
     Common::DetachedTasks detached_tasks;
@@ -75,6 +144,7 @@ int main(int argc, char** argv) {
     std::string username;
     std::string token;
     std::string web_api_url;
+    std::string ban_list_file;
     u64 preferred_game_id = 0;
     u32 port = Network::DefaultRoomPort;
     u32 max_members = 16;
@@ -90,6 +160,7 @@ int main(int argc, char** argv) {
         {"username", required_argument, 0, 'u'},
         {"token", required_argument, 0, 't'},
         {"web-api-url", required_argument, 0, 'a'},
+        {"ban-list-file", required_argument, 0, 'b'},
         {"help", no_argument, 0, 'h'},
         {"version", no_argument, 0, 'v'},
         {0, 0, 0, 0},
@@ -129,6 +200,9 @@ int main(int argc, char** argv) {
             case 'a':
                 web_api_url.assign(optarg);
                 break;
+            case 'b':
+                ban_list_file.assign(optarg);
+                break;
             case 'h':
                 PrintHelp(argv[0]);
                 return 0;
@@ -164,6 +238,10 @@ int main(int argc, char** argv) {
         PrintHelp(argv[0]);
         return -1;
     }
+    if (ban_list_file.empty()) {
+        std::cout << "Ban list file not set!\nThis should get set to load and save room ban "
+                     "list.\nSet with --ban-list-file <file>\n\n";
+    }
     bool announce = true;
     if (username.empty()) {
         announce = false;
@@ -184,6 +262,12 @@ int main(int argc, char** argv) {
         Settings::values.citra_token = token;
     }
 
+    // Load the ban list
+    Network::Room::BanList ban_list;
+    if (!ban_list_file.empty()) {
+        ban_list = LoadBanList(ban_list_file);
+    }
+
     std::unique_ptr<Network::VerifyUser::Backend> verify_backend;
     if (announce) {
 #ifdef ENABLE_WEB_SERVICE
@@ -199,8 +283,8 @@ int main(int argc, char** argv) {
 
     Network::Init();
     if (std::shared_ptr<Network::Room> room = Network::GetRoom().lock()) {
-        if (!room->Create(room_name, room_description, "", port, password, max_members,
-                          preferred_game, preferred_game_id, std::move(verify_backend))) {
+        if (!room->Create(room_name, room_description, "", port, password, max_members, username,
+                          preferred_game, preferred_game_id, std::move(verify_backend), ban_list)) {
             std::cout << "Failed to create room: \n\n";
             return -1;
         }
@@ -217,6 +301,10 @@ int main(int argc, char** argv) {
                     announce_session->Stop();
                 }
                 announce_session.reset();
+                // Save the ban list
+                if (!ban_list_file.empty()) {
+                    SaveBanList(room->GetBanList(), ban_list_file);
+                }
                 room->Destroy();
                 Network::Shutdown();
                 return 0;
@@ -227,6 +315,10 @@ int main(int argc, char** argv) {
             announce_session->Stop();
         }
         announce_session.reset();
+        // Save the ban list
+        if (!ban_list_file.empty()) {
+            SaveBanList(room->GetBanList(), ban_list_file);
+        }
         room->Destroy();
     }
     Network::Shutdown();