diff --git a/src/app.rs b/src/app.rs
index 14dc0b07f..64119aa34 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -21,19 +21,14 @@
 //! - UDP trackers: the user can enable multiple UDP tracker on several ports.
 //! - HTTP trackers: the user can enable multiple HTTP tracker on several ports.
 //! - Tracker REST API: the tracker API can be enabled/disabled.
-use std::sync::Arc;
-
-use tokio::sync::RwLock;
 use tokio::task::JoinHandle;
 use torrust_tracker_configuration::Configuration;
 use tracing::instrument;
 
 use crate::bootstrap::jobs::{health_check_api, http_tracker, torrent_cleanup, tracker_apis, udp_tracker};
-use crate::core::statistics::event::sender::Sender;
-use crate::core::statistics::repository::Repository;
+use crate::container::AppContainer;
+use crate::servers;
 use crate::servers::registar::Registar;
-use crate::servers::udp::server::banning::BanService;
-use crate::{core, servers};
 
 /// # Panics
 ///
@@ -41,14 +36,8 @@ use crate::{core, servers};
 ///
 /// - Can't retrieve tracker keys from database.
 /// - Can't load whitelist from database.
-#[instrument(skip(config, tracker, ban_service, stats_event_sender, stats_repository))]
-pub async fn start(
-    config: &Configuration,
-    tracker: Arc<core::Tracker>,
-    ban_service: Arc<RwLock<BanService>>,
-    stats_event_sender: Arc<Option<Box<dyn Sender>>>,
-    stats_repository: Arc<Repository>,
-) -> Vec<JoinHandle<()>> {
+#[instrument(skip(config, app_container))]
+pub async fn start(config: &Configuration, app_container: &AppContainer) -> Vec<JoinHandle<()>> {
     if config.http_api.is_none()
         && (config.udp_trackers.is_none() || config.udp_trackers.as_ref().map_or(true, std::vec::Vec::is_empty))
         && (config.http_trackers.is_none() || config.http_trackers.as_ref().map_or(true, std::vec::Vec::is_empty))
@@ -61,16 +50,18 @@ pub async fn start(
     let registar = Registar::default();
 
     // Load peer keys
-    if tracker.is_private() {
-        tracker
+    if app_container.tracker.is_private() {
+        app_container
+            .tracker
             .load_keys_from_database()
             .await
             .expect("Could not retrieve keys from database.");
     }
 
     // Load whitelisted torrents
-    if tracker.is_listed() {
-        tracker
+    if app_container.tracker.is_listed() {
+        app_container
+            .tracker
             .whitelist_manager
             .load_whitelist_from_database()
             .await
@@ -80,7 +71,7 @@ pub async fn start(
     // Start the UDP blocks
     if let Some(udp_trackers) = &config.udp_trackers {
         for udp_tracker_config in udp_trackers {
-            if tracker.is_private() {
+            if app_container.tracker.is_private() {
                 tracing::warn!(
                     "Could not start UDP tracker on: {} while in private mode. UDP is not safe for private trackers!",
                     udp_tracker_config.bind_address
@@ -89,9 +80,9 @@ pub async fn start(
                 jobs.push(
                     udp_tracker::start_job(
                         udp_tracker_config,
-                        tracker.clone(),
-                        stats_event_sender.clone(),
-                        ban_service.clone(),
+                        app_container.tracker.clone(),
+                        app_container.stats_event_sender.clone(),
+                        app_container.ban_service.clone(),
                         registar.give_form(),
                     )
                     .await,
@@ -107,8 +98,8 @@ pub async fn start(
         for http_tracker_config in http_trackers {
             if let Some(job) = http_tracker::start_job(
                 http_tracker_config,
-                tracker.clone(),
-                stats_event_sender.clone(),
+                app_container.tracker.clone(),
+                app_container.stats_event_sender.clone(),
                 registar.give_form(),
                 servers::http::Version::V1,
             )
@@ -125,10 +116,10 @@ pub async fn start(
     if let Some(http_api_config) = &config.http_api {
         if let Some(job) = tracker_apis::start_job(
             http_api_config,
-            tracker.clone(),
-            ban_service.clone(),
-            stats_event_sender.clone(),
-            stats_repository.clone(),
+            app_container.tracker.clone(),
+            app_container.ban_service.clone(),
+            app_container.stats_event_sender.clone(),
+            app_container.stats_repository.clone(),
             registar.give_form(),
             servers::apis::Version::V1,
         )
@@ -142,7 +133,7 @@ pub async fn start(
 
     // Start runners to remove torrents without peers, every interval
     if config.core.inactive_peer_cleanup_interval > 0 {
-        jobs.push(torrent_cleanup::start_job(&config.core, &tracker));
+        jobs.push(torrent_cleanup::start_job(&config.core, &app_container.tracker));
     }
 
     // Start Health Check API
diff --git a/src/app_test.rs b/src/app_test.rs
new file mode 100644
index 000000000..c50f87965
--- /dev/null
+++ b/src/app_test.rs
@@ -0,0 +1,18 @@
+//! This file contains only functions used for testing.
+use std::sync::Arc;
+
+use torrust_tracker_configuration::Configuration;
+
+use crate::core::databases::Database;
+use crate::core::services::{initialize_database, initialize_whitelist};
+use crate::core::whitelist::WhiteListManager;
+
+/// Initialize the tracker dependencies.
+#[allow(clippy::type_complexity)]
+#[must_use]
+pub fn initialize_tracker_dependencies(config: &Configuration) -> (Arc<Box<dyn Database>>, Arc<WhiteListManager>) {
+    let database = initialize_database(config);
+    let whitelist_manager = initialize_whitelist(database.clone());
+
+    (database, whitelist_manager)
+}
diff --git a/src/bootstrap/app.rs b/src/bootstrap/app.rs
index d63b414e1..53bf44f79 100644
--- a/src/bootstrap/app.rs
+++ b/src/bootstrap/app.rs
@@ -21,32 +21,21 @@ use tracing::instrument;
 
 use super::config::initialize_configuration;
 use crate::bootstrap;
-use crate::core::databases::Database;
-use crate::core::services::{initialize_database, initialize_whitelist, statistics, tracker_factory};
-use crate::core::statistics::event::sender::Sender;
-use crate::core::statistics::repository::Repository;
-use crate::core::whitelist::WhiteListManager;
-use crate::core::Tracker;
+use crate::container::AppContainer;
+use crate::core::services::{initialize_database, initialize_tracker, initialize_whitelist, statistics};
 use crate::servers::udp::server::banning::BanService;
 use crate::servers::udp::server::launcher::MAX_CONNECTION_ID_ERRORS_PER_IP;
 use crate::shared::crypto::ephemeral_instance_keys;
 use crate::shared::crypto::keys::{self, Keeper as _};
 
-/// It loads the configuration from the environment and builds the main domain [`Tracker`] struct.
+/// It loads the configuration from the environment and builds app container.
 ///
 /// # Panics
 ///
 /// Setup can file if the configuration is invalid.
 #[must_use]
-#[allow(clippy::type_complexity)]
 #[instrument(skip())]
-pub fn setup() -> (
-    Configuration,
-    Arc<Tracker>,
-    Arc<RwLock<BanService>>,
-    Arc<Option<Box<dyn Sender>>>,
-    Arc<Repository>,
-) {
+pub fn setup() -> (Configuration, AppContainer) {
     #[cfg(not(test))]
     check_seed();
 
@@ -56,19 +45,13 @@ pub fn setup() -> (
         panic!("Configuration error: {e}");
     }
 
-    // Initialize services
-
-    let (stats_event_sender, stats_repository) = statistics::setup::factory(configuration.core.tracker_usage_statistics);
-    let stats_event_sender = Arc::new(stats_event_sender);
-    let stats_repository = Arc::new(stats_repository);
-
-    let udp_ban_service = Arc::new(RwLock::new(BanService::new(MAX_CONNECTION_ID_ERRORS_PER_IP)));
-
-    let tracker = initialize_with_configuration(&configuration);
+    initialize_global_services(&configuration);
 
     tracing::info!("Configuration:\n{}", configuration.clone().mask_secrets().to_json());
 
-    (configuration, tracker, udp_ban_service, stats_event_sender, stats_repository)
+    let app_container = initialize_app_container(&configuration);
+
+    (configuration, app_container)
 }
 
 /// checks if the seed is the instance seed in production.
@@ -83,15 +66,31 @@ pub fn check_seed() {
     assert_eq!(seed, instance, "maybe using zeroed seed in production!?");
 }
 
-/// It initializes the application with the given configuration.
-///
-/// The configuration may be obtained from the environment (via config file or env vars).
-#[must_use]
+/// It initializes the global services.
 #[instrument(skip())]
-pub fn initialize_with_configuration(configuration: &Configuration) -> Arc<Tracker> {
+pub fn initialize_global_services(configuration: &Configuration) {
     initialize_static();
     initialize_logging(configuration);
-    Arc::new(initialize_tracker(configuration))
+}
+
+/// It initializes the IoC Container.
+#[instrument(skip())]
+pub fn initialize_app_container(configuration: &Configuration) -> AppContainer {
+    let (stats_event_sender, stats_repository) = statistics::setup::factory(configuration.core.tracker_usage_statistics);
+    let stats_event_sender = Arc::new(stats_event_sender);
+    let stats_repository = Arc::new(stats_repository);
+    let ban_service = Arc::new(RwLock::new(BanService::new(MAX_CONNECTION_ID_ERRORS_PER_IP)));
+    let database = initialize_database(configuration);
+    let whitelist_manager = initialize_whitelist(database.clone());
+    let tracker = Arc::new(initialize_tracker(configuration, &database, &whitelist_manager));
+
+    AppContainer {
+        tracker,
+        ban_service,
+        stats_event_sender,
+        stats_repository,
+        whitelist_manager,
+    }
 }
 
 /// It initializes the application static values.
@@ -115,27 +114,6 @@ pub fn initialize_static() {
     lazy_static::initialize(&ephemeral_instance_keys::ZEROED_TEST_CIPHER_BLOWFISH);
 }
 
-/// It builds the domain tracker
-///
-/// The tracker is the domain layer service. It's the entrypoint to make requests to the domain layer.
-/// It's used by other higher-level components like the UDP and HTTP trackers or the tracker API.
-#[must_use]
-#[instrument(skip(config))]
-pub fn initialize_tracker(config: &Configuration) -> Tracker {
-    let (database, whitelist_manager) = initialize_tracker_dependencies(config);
-
-    tracker_factory(config, &database, &whitelist_manager)
-}
-
-#[allow(clippy::type_complexity)]
-#[must_use]
-pub fn initialize_tracker_dependencies(config: &Configuration) -> (Arc<Box<dyn Database>>, Arc<WhiteListManager>) {
-    let database = initialize_database(config);
-    let whitelist_manager = initialize_whitelist(database.clone());
-
-    (database, whitelist_manager)
-}
-
 /// It initializes the log threshold, format and channel.
 ///
 /// See [the logging setup](crate::bootstrap::logging::setup) for more info about logging.
diff --git a/src/bootstrap/jobs/http_tracker.rs b/src/bootstrap/jobs/http_tracker.rs
index 9135a8828..d32e8d4aa 100644
--- a/src/bootstrap/jobs/http_tracker.rs
+++ b/src/bootstrap/jobs/http_tracker.rs
@@ -86,9 +86,9 @@ mod tests {
 
     use torrust_tracker_test_helpers::configuration::ephemeral_public;
 
-    use crate::bootstrap::app::initialize_with_configuration;
+    use crate::bootstrap::app::initialize_global_services;
     use crate::bootstrap::jobs::http_tracker::start_job;
-    use crate::core::services::statistics;
+    use crate::core::services::{initialize_database, initialize_tracker, initialize_whitelist, statistics};
     use crate::servers::http::Version;
     use crate::servers::registar::Registar;
 
@@ -97,9 +97,16 @@ mod tests {
         let cfg = Arc::new(ephemeral_public());
         let http_tracker = cfg.http_trackers.clone().expect("missing HTTP tracker configuration");
         let config = &http_tracker[0];
+
         let (stats_event_sender, _stats_repository) = statistics::setup::factory(cfg.core.tracker_usage_statistics);
         let stats_event_sender = Arc::new(stats_event_sender);
-        let tracker = initialize_with_configuration(&cfg);
+
+        initialize_global_services(&cfg);
+
+        let database = initialize_database(&cfg);
+        let whitelist_manager = initialize_whitelist(database.clone());
+        let tracker = Arc::new(initialize_tracker(&cfg, &database, &whitelist_manager));
+
         let version = Version::V1;
 
         start_job(config, tracker, stats_event_sender, Registar::default().give_form(), version)
diff --git a/src/bootstrap/jobs/tracker_apis.rs b/src/bootstrap/jobs/tracker_apis.rs
index d84bb08a9..9c284fbfc 100644
--- a/src/bootstrap/jobs/tracker_apis.rs
+++ b/src/bootstrap/jobs/tracker_apis.rs
@@ -140,9 +140,9 @@ mod tests {
     use tokio::sync::RwLock;
     use torrust_tracker_test_helpers::configuration::ephemeral_public;
 
-    use crate::bootstrap::app::initialize_with_configuration;
+    use crate::bootstrap::app::initialize_global_services;
     use crate::bootstrap::jobs::tracker_apis::start_job;
-    use crate::core::services::statistics;
+    use crate::core::services::{initialize_database, initialize_tracker, initialize_whitelist, statistics};
     use crate::servers::apis::Version;
     use crate::servers::registar::Registar;
     use crate::servers::udp::server::banning::BanService;
@@ -158,7 +158,11 @@ mod tests {
         let stats_event_sender = Arc::new(stats_event_sender);
         let stats_repository = Arc::new(stats_repository);
 
-        let tracker = initialize_with_configuration(&cfg);
+        initialize_global_services(&cfg);
+
+        let database = initialize_database(&cfg);
+        let whitelist_manager = initialize_whitelist(database.clone());
+        let tracker = Arc::new(initialize_tracker(&cfg, &database, &whitelist_manager));
 
         let version = Version::V1;
 
diff --git a/src/console/profiling.rs b/src/console/profiling.rs
index 2f6471906..318fce1e8 100644
--- a/src/console/profiling.rs
+++ b/src/console/profiling.rs
@@ -179,9 +179,9 @@ pub async fn run() {
         return;
     };
 
-    let (config, tracker, ban_service, stats_event_sender, stats_repository) = bootstrap::app::setup();
+    let (config, app_container) = bootstrap::app::setup();
 
-    let jobs = app::start(&config, tracker, ban_service, stats_event_sender, stats_repository).await;
+    let jobs = app::start(&config, &app_container).await;
 
     // Run the tracker for a fixed duration
     let run_duration = sleep(Duration::from_secs(duration_secs));
diff --git a/src/container.rs b/src/container.rs
new file mode 100644
index 000000000..7a2b86d18
--- /dev/null
+++ b/src/container.rs
@@ -0,0 +1,17 @@
+use std::sync::Arc;
+
+use tokio::sync::RwLock;
+
+use crate::core::statistics::event::sender::Sender;
+use crate::core::statistics::repository::Repository;
+use crate::core::whitelist::WhiteListManager;
+use crate::core::Tracker;
+use crate::servers::udp::server::banning::BanService;
+
+pub struct AppContainer {
+    pub tracker: Arc<Tracker>,
+    pub ban_service: Arc<RwLock<BanService>>,
+    pub stats_event_sender: Arc<Option<Box<dyn Sender>>>,
+    pub stats_repository: Arc<Repository>,
+    pub whitelist_manager: Arc<WhiteListManager>,
+}
diff --git a/src/core/mod.rs b/src/core/mod.rs
index 9aef1b2f2..e802b2c43 100644
--- a/src/core/mod.rs
+++ b/src/core/mod.rs
@@ -1080,28 +1080,28 @@ mod tests {
         use torrust_tracker_primitives::DurationSinceUnixEpoch;
         use torrust_tracker_test_helpers::configuration;
 
-        use crate::bootstrap::app::initialize_tracker_dependencies;
+        use crate::app_test::initialize_tracker_dependencies;
         use crate::core::peer::Peer;
-        use crate::core::services::tracker_factory;
+        use crate::core::services::initialize_tracker;
         use crate::core::whitelist::WhiteListManager;
         use crate::core::{TorrentsMetrics, Tracker};
 
         fn public_tracker() -> Tracker {
             let config = configuration::ephemeral_public();
             let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
-            tracker_factory(&config, &database, &whitelist_manager)
+            initialize_tracker(&config, &database, &whitelist_manager)
         }
 
         fn private_tracker() -> Tracker {
             let config = configuration::ephemeral_private();
             let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
-            tracker_factory(&config, &database, &whitelist_manager)
+            initialize_tracker(&config, &database, &whitelist_manager)
         }
 
         fn whitelisted_tracker() -> (Tracker, Arc<WhiteListManager>) {
             let config = configuration::ephemeral_listed();
             let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
-            let tracker = tracker_factory(&config, &database, &whitelist_manager);
+            let tracker = initialize_tracker(&config, &database, &whitelist_manager);
 
             (tracker, whitelist_manager)
         }
@@ -1110,7 +1110,7 @@ mod tests {
             let mut config = configuration::ephemeral_listed();
             config.core.tracker_policy.persistent_torrent_completed_stat = true;
             let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
-            tracker_factory(&config, &database, &whitelist_manager)
+            initialize_tracker(&config, &database, &whitelist_manager)
         }
 
         fn sample_info_hash() -> InfoHash {
diff --git a/src/core/services/mod.rs b/src/core/services/mod.rs
index d3336068c..fd301b62d 100644
--- a/src/core/services/mod.rs
+++ b/src/core/services/mod.rs
@@ -24,7 +24,7 @@ use crate::core::Tracker;
 ///
 /// Will panic if tracker cannot be instantiated.
 #[must_use]
-pub fn tracker_factory(
+pub fn initialize_tracker(
     config: &Configuration,
     database: &Arc<Box<dyn Database>>,
     whitelist_manager: &Arc<WhiteListManager>,
diff --git a/src/core/services/statistics/mod.rs b/src/core/services/statistics/mod.rs
index 657f3eb06..1e0403c2a 100644
--- a/src/core/services/statistics/mod.rs
+++ b/src/core/services/statistics/mod.rs
@@ -117,10 +117,10 @@ mod tests {
     use torrust_tracker_primitives::torrent_metrics::TorrentsMetrics;
     use torrust_tracker_test_helpers::configuration;
 
-    use crate::bootstrap::app::initialize_tracker_dependencies;
+    use crate::app_test::initialize_tracker_dependencies;
     use crate::core;
+    use crate::core::services::initialize_tracker;
     use crate::core::services::statistics::{self, get_metrics, TrackerMetrics};
-    use crate::core::services::tracker_factory;
     use crate::servers::udp::server::banning::BanService;
     use crate::servers::udp::server::launcher::MAX_CONNECTION_ID_ERRORS_PER_IP;
 
@@ -136,7 +136,7 @@ mod tests {
         let (_stats_event_sender, stats_repository) = statistics::setup::factory(config.core.tracker_usage_statistics);
         let stats_repository = Arc::new(stats_repository);
 
-        let tracker = Arc::new(tracker_factory(&config, &database, &whitelist_manager));
+        let tracker = Arc::new(initialize_tracker(&config, &database, &whitelist_manager));
 
         let ban_service = Arc::new(RwLock::new(BanService::new(MAX_CONNECTION_ID_ERRORS_PER_IP)));
 
diff --git a/src/core/services/torrent.rs b/src/core/services/torrent.rs
index 9a1a2a725..593d8be8c 100644
--- a/src/core/services/torrent.rs
+++ b/src/core/services/torrent.rs
@@ -129,10 +129,10 @@ mod tests {
         use torrust_tracker_configuration::Configuration;
         use torrust_tracker_test_helpers::configuration;
 
-        use crate::bootstrap::app::initialize_tracker_dependencies;
+        use crate::app_test::initialize_tracker_dependencies;
+        use crate::core::services::initialize_tracker;
         use crate::core::services::torrent::tests::sample_peer;
         use crate::core::services::torrent::{get_torrent_info, Info};
-        use crate::core::services::tracker_factory;
 
         pub fn tracker_configuration() -> Configuration {
             configuration::ephemeral()
@@ -143,7 +143,7 @@ mod tests {
             let config = tracker_configuration();
 
             let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
-            let tracker = tracker_factory(&config, &database, &whitelist_manager);
+            let tracker = initialize_tracker(&config, &database, &whitelist_manager);
 
             let tracker = Arc::new(tracker);
 
@@ -161,7 +161,7 @@ mod tests {
             let config = tracker_configuration();
 
             let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
-            let tracker = Arc::new(tracker_factory(&config, &database, &whitelist_manager));
+            let tracker = Arc::new(initialize_tracker(&config, &database, &whitelist_manager));
 
             let hash = "9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d".to_owned();
             let info_hash = InfoHash::from_str(&hash).unwrap();
@@ -191,10 +191,10 @@ mod tests {
         use torrust_tracker_configuration::Configuration;
         use torrust_tracker_test_helpers::configuration;
 
-        use crate::bootstrap::app::initialize_tracker_dependencies;
+        use crate::app_test::initialize_tracker_dependencies;
+        use crate::core::services::initialize_tracker;
         use crate::core::services::torrent::tests::sample_peer;
         use crate::core::services::torrent::{get_torrents_page, BasicInfo, Pagination};
-        use crate::core::services::tracker_factory;
 
         pub fn tracker_configuration() -> Configuration {
             configuration::ephemeral()
@@ -205,7 +205,7 @@ mod tests {
             let config = tracker_configuration();
 
             let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
-            let tracker = Arc::new(tracker_factory(&config, &database, &whitelist_manager));
+            let tracker = Arc::new(initialize_tracker(&config, &database, &whitelist_manager));
 
             let torrents = get_torrents_page(tracker.clone(), Some(&Pagination::default())).await;
 
@@ -217,7 +217,7 @@ mod tests {
             let config = tracker_configuration();
 
             let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
-            let tracker = Arc::new(tracker_factory(&config, &database, &whitelist_manager));
+            let tracker = Arc::new(initialize_tracker(&config, &database, &whitelist_manager));
 
             let hash = "9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d".to_owned();
             let info_hash = InfoHash::from_str(&hash).unwrap();
@@ -242,7 +242,7 @@ mod tests {
             let config = tracker_configuration();
 
             let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
-            let tracker = Arc::new(tracker_factory(&config, &database, &whitelist_manager));
+            let tracker = Arc::new(initialize_tracker(&config, &database, &whitelist_manager));
 
             let hash1 = "9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d".to_owned();
             let info_hash1 = InfoHash::from_str(&hash1).unwrap();
@@ -265,7 +265,7 @@ mod tests {
             let config = tracker_configuration();
 
             let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
-            let tracker = Arc::new(tracker_factory(&config, &database, &whitelist_manager));
+            let tracker = Arc::new(initialize_tracker(&config, &database, &whitelist_manager));
 
             let hash1 = "9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d".to_owned();
             let info_hash1 = InfoHash::from_str(&hash1).unwrap();
@@ -297,7 +297,7 @@ mod tests {
             let config = tracker_configuration();
 
             let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
-            let tracker = Arc::new(tracker_factory(&config, &database, &whitelist_manager));
+            let tracker = Arc::new(initialize_tracker(&config, &database, &whitelist_manager));
 
             let hash1 = "9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d".to_owned();
             let info_hash1 = InfoHash::from_str(&hash1).unwrap();
diff --git a/src/lib.rs b/src/lib.rs
index d7e4bc5b2..8e0e64db0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -491,8 +491,10 @@
 use torrust_tracker_clock::clock;
 
 pub mod app;
+pub mod app_test;
 pub mod bootstrap;
 pub mod console;
+pub mod container;
 pub mod core;
 pub mod servers;
 pub mod shared;
diff --git a/src/main.rs b/src/main.rs
index e536124a2..f05de0327 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,9 +2,9 @@ use torrust_tracker_lib::{app, bootstrap};
 
 #[tokio::main]
 async fn main() {
-    let (config, tracker, udp_ban_service, stats_event_sender, stats_repository) = bootstrap::app::setup();
+    let (config, app_container) = bootstrap::app::setup();
 
-    let jobs = app::start(&config, tracker, udp_ban_service, stats_event_sender, stats_repository).await;
+    let jobs = app::start(&config, &app_container).await;
 
     // handle the signals
     tokio::select! {
diff --git a/src/servers/apis/server.rs b/src/servers/apis/server.rs
index bf1511edb..c4fae6ebf 100644
--- a/src/servers/apis/server.rs
+++ b/src/servers/apis/server.rs
@@ -333,9 +333,9 @@ mod tests {
     use tokio::sync::RwLock;
     use torrust_tracker_test_helpers::configuration::ephemeral_public;
 
-    use crate::bootstrap::app::initialize_with_configuration;
+    use crate::bootstrap::app::initialize_global_services;
     use crate::bootstrap::jobs::make_rust_tls;
-    use crate::core::services::statistics;
+    use crate::core::services::{initialize_database, initialize_tracker, initialize_whitelist, statistics};
     use crate::servers::apis::server::{ApiServer, Launcher};
     use crate::servers::registar::Registar;
     use crate::servers::udp::server::banning::BanService;
@@ -350,7 +350,12 @@ mod tests {
         let (stats_event_sender, stats_repository) = statistics::setup::factory(cfg.core.tracker_usage_statistics);
         let stats_event_sender = Arc::new(stats_event_sender);
         let stats_repository = Arc::new(stats_repository);
-        let tracker = initialize_with_configuration(&cfg);
+
+        initialize_global_services(&cfg);
+
+        let database = initialize_database(&cfg);
+        let whitelist_manager = initialize_whitelist(database.clone());
+        let tracker = Arc::new(initialize_tracker(&cfg, &database, &whitelist_manager));
 
         let bind_to = config.bind_address;
 
diff --git a/src/servers/http/server.rs b/src/servers/http/server.rs
index 537fc37fb..82b65c2ff 100644
--- a/src/servers/http/server.rs
+++ b/src/servers/http/server.rs
@@ -242,9 +242,9 @@ mod tests {
 
     use torrust_tracker_test_helpers::configuration::ephemeral_public;
 
-    use crate::bootstrap::app::initialize_with_configuration;
+    use crate::bootstrap::app::initialize_global_services;
     use crate::bootstrap::jobs::make_rust_tls;
-    use crate::core::services::statistics;
+    use crate::core::services::{initialize_database, initialize_tracker, initialize_whitelist, statistics};
     use crate::servers::http::server::{HttpServer, Launcher};
     use crate::servers::registar::Registar;
 
@@ -254,7 +254,12 @@ mod tests {
 
         let (stats_event_sender, _stats_repository) = statistics::setup::factory(cfg.core.tracker_usage_statistics);
         let stats_event_sender = Arc::new(stats_event_sender);
-        let tracker = initialize_with_configuration(&cfg);
+
+        initialize_global_services(&cfg);
+
+        let database = initialize_database(&cfg);
+        let whitelist_manager = initialize_whitelist(database.clone());
+        let tracker = Arc::new(initialize_tracker(&cfg, &database, &whitelist_manager));
 
         let http_trackers = cfg.http_trackers.clone().expect("missing HTTP trackers configuration");
         let config = &http_trackers[0];
diff --git a/src/servers/http/v1/handlers/announce.rs b/src/servers/http/v1/handlers/announce.rs
index 1c8779625..24beadbc2 100644
--- a/src/servers/http/v1/handlers/announce.rs
+++ b/src/servers/http/v1/handlers/announce.rs
@@ -205,8 +205,8 @@ mod tests {
     use bittorrent_primitives::info_hash::InfoHash;
     use torrust_tracker_test_helpers::configuration;
 
-    use crate::bootstrap::app::initialize_tracker_dependencies;
-    use crate::core::services::{statistics, tracker_factory};
+    use crate::app_test::initialize_tracker_dependencies;
+    use crate::core::services::{initialize_tracker, statistics};
     use crate::core::statistics::event::sender::Sender;
     use crate::core::Tracker;
 
@@ -216,7 +216,7 @@ mod tests {
         let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
         let (stats_event_sender, _stats_repository) = statistics::setup::factory(config.core.tracker_usage_statistics);
 
-        (tracker_factory(&config, &database, &whitelist_manager), stats_event_sender)
+        (initialize_tracker(&config, &database, &whitelist_manager), stats_event_sender)
     }
 
     fn whitelisted_tracker() -> (Tracker, Option<Box<dyn Sender>>) {
@@ -225,7 +225,7 @@ mod tests {
         let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
         let (stats_event_sender, _stats_repository) = statistics::setup::factory(config.core.tracker_usage_statistics);
 
-        (tracker_factory(&config, &database, &whitelist_manager), stats_event_sender)
+        (initialize_tracker(&config, &database, &whitelist_manager), stats_event_sender)
     }
 
     fn tracker_on_reverse_proxy() -> (Tracker, Option<Box<dyn Sender>>) {
@@ -234,7 +234,7 @@ mod tests {
         let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
         let (stats_event_sender, _stats_repository) = statistics::setup::factory(config.core.tracker_usage_statistics);
 
-        (tracker_factory(&config, &database, &whitelist_manager), stats_event_sender)
+        (initialize_tracker(&config, &database, &whitelist_manager), stats_event_sender)
     }
 
     fn tracker_not_on_reverse_proxy() -> (Tracker, Option<Box<dyn Sender>>) {
@@ -243,7 +243,7 @@ mod tests {
         let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
         let (stats_event_sender, _stats_repository) = statistics::setup::factory(config.core.tracker_usage_statistics);
 
-        (tracker_factory(&config, &database, &whitelist_manager), stats_event_sender)
+        (initialize_tracker(&config, &database, &whitelist_manager), stats_event_sender)
     }
 
     fn sample_announce_request() -> Announce {
diff --git a/src/servers/http/v1/handlers/scrape.rs b/src/servers/http/v1/handlers/scrape.rs
index 6ff8a61cf..a5cf58129 100644
--- a/src/servers/http/v1/handlers/scrape.rs
+++ b/src/servers/http/v1/handlers/scrape.rs
@@ -126,8 +126,8 @@ mod tests {
     use bittorrent_primitives::info_hash::InfoHash;
     use torrust_tracker_test_helpers::configuration;
 
-    use crate::bootstrap::app::initialize_tracker_dependencies;
-    use crate::core::services::{statistics, tracker_factory};
+    use crate::app_test::initialize_tracker_dependencies;
+    use crate::core::services::{initialize_tracker, statistics};
     use crate::core::Tracker;
 
     fn private_tracker() -> (Tracker, Option<Box<dyn crate::core::statistics::event::sender::Sender>>) {
@@ -136,7 +136,7 @@ mod tests {
         let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
         let (stats_event_sender, _stats_repository) = statistics::setup::factory(config.core.tracker_usage_statistics);
 
-        (tracker_factory(&config, &database, &whitelist_manager), stats_event_sender)
+        (initialize_tracker(&config, &database, &whitelist_manager), stats_event_sender)
     }
 
     fn whitelisted_tracker() -> (Tracker, Option<Box<dyn crate::core::statistics::event::sender::Sender>>) {
@@ -145,7 +145,7 @@ mod tests {
         let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
         let (stats_event_sender, _stats_repository) = statistics::setup::factory(config.core.tracker_usage_statistics);
 
-        (tracker_factory(&config, &database, &whitelist_manager), stats_event_sender)
+        (initialize_tracker(&config, &database, &whitelist_manager), stats_event_sender)
     }
 
     fn tracker_on_reverse_proxy() -> (Tracker, Option<Box<dyn crate::core::statistics::event::sender::Sender>>) {
@@ -154,7 +154,7 @@ mod tests {
         let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
         let (stats_event_sender, _stats_repository) = statistics::setup::factory(config.core.tracker_usage_statistics);
 
-        (tracker_factory(&config, &database, &whitelist_manager), stats_event_sender)
+        (initialize_tracker(&config, &database, &whitelist_manager), stats_event_sender)
     }
 
     fn tracker_not_on_reverse_proxy() -> (Tracker, Option<Box<dyn crate::core::statistics::event::sender::Sender>>) {
@@ -163,7 +163,7 @@ mod tests {
         let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
         let (stats_event_sender, _stats_repository) = statistics::setup::factory(config.core.tracker_usage_statistics);
 
-        (tracker_factory(&config, &database, &whitelist_manager), stats_event_sender)
+        (initialize_tracker(&config, &database, &whitelist_manager), stats_event_sender)
     }
 
     fn sample_scrape_request() -> Scrape {
diff --git a/src/servers/http/v1/services/announce.rs b/src/servers/http/v1/services/announce.rs
index 45bcb5843..63a904182 100644
--- a/src/servers/http/v1/services/announce.rs
+++ b/src/servers/http/v1/services/announce.rs
@@ -65,8 +65,8 @@ mod tests {
     use torrust_tracker_primitives::{peer, DurationSinceUnixEpoch};
     use torrust_tracker_test_helpers::configuration;
 
-    use crate::bootstrap::app::initialize_tracker_dependencies;
-    use crate::core::services::{statistics, tracker_factory};
+    use crate::app_test::initialize_tracker_dependencies;
+    use crate::core::services::{initialize_tracker, statistics};
     use crate::core::statistics::event::sender::Sender;
     use crate::core::Tracker;
 
@@ -77,7 +77,7 @@ mod tests {
         let (stats_event_sender, _stats_repository) = statistics::setup::factory(config.core.tracker_usage_statistics);
         let stats_event_sender = Arc::new(stats_event_sender);
 
-        let tracker = tracker_factory(&config, &database, &whitelist_manager);
+        let tracker = initialize_tracker(&config, &database, &whitelist_manager);
 
         (tracker, stats_event_sender)
     }
@@ -123,7 +123,7 @@ mod tests {
         use torrust_tracker_test_helpers::configuration;
 
         use super::{sample_peer_using_ipv4, sample_peer_using_ipv6};
-        use crate::bootstrap::app::initialize_tracker_dependencies;
+        use crate::app_test::initialize_tracker_dependencies;
         use crate::core::{statistics, PeersWanted, Tracker};
         use crate::servers::http::v1::services::announce::invoke;
         use crate::servers::http::v1::services::announce::tests::{public_tracker, sample_info_hash, sample_peer};
diff --git a/src/servers/http/v1/services/scrape.rs b/src/servers/http/v1/services/scrape.rs
index 9805dd8a4..56c18cbb3 100644
--- a/src/servers/http/v1/services/scrape.rs
+++ b/src/servers/http/v1/services/scrape.rs
@@ -80,8 +80,8 @@ mod tests {
     use torrust_tracker_primitives::{peer, DurationSinceUnixEpoch};
     use torrust_tracker_test_helpers::configuration;
 
-    use crate::bootstrap::app::initialize_tracker_dependencies;
-    use crate::core::services::tracker_factory;
+    use crate::app_test::initialize_tracker_dependencies;
+    use crate::core::services::initialize_tracker;
     use crate::core::Tracker;
 
     fn public_tracker() -> Tracker {
@@ -89,7 +89,7 @@ mod tests {
 
         let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
 
-        tracker_factory(&config, &database, &whitelist_manager)
+        initialize_tracker(&config, &database, &whitelist_manager)
     }
 
     fn sample_info_hashes() -> Vec<InfoHash> {
diff --git a/src/servers/udp/handlers.rs b/src/servers/udp/handlers.rs
index 9883de54b..a7d964391 100644
--- a/src/servers/udp/handlers.rs
+++ b/src/servers/udp/handlers.rs
@@ -470,8 +470,8 @@ mod tests {
     use torrust_tracker_test_helpers::configuration;
 
     use super::gen_remote_fingerprint;
-    use crate::bootstrap::app::initialize_tracker_dependencies;
-    use crate::core::services::{statistics, tracker_factory};
+    use crate::app_test::initialize_tracker_dependencies;
+    use crate::core::services::{initialize_tracker, statistics};
     use crate::core::statistics::event::sender::Sender;
     use crate::core::Tracker;
     use crate::CurrentClock;
@@ -496,7 +496,7 @@ mod tests {
         let (database, whitelist_manager) = initialize_tracker_dependencies(config);
         let (stats_event_sender, _stats_repository) = statistics::setup::factory(config.core.tracker_usage_statistics);
 
-        (tracker_factory(config, &database, &whitelist_manager), stats_event_sender)
+        (initialize_tracker(config, &database, &whitelist_manager), stats_event_sender)
     }
 
     fn sample_ipv4_remote_addr() -> SocketAddr {
@@ -1318,7 +1318,7 @@ mod tests {
                 use aquatic_udp_protocol::{InfoHash as AquaticInfoHash, PeerId as AquaticPeerId};
                 use mockall::predicate::eq;
 
-                use crate::bootstrap::app::initialize_tracker_dependencies;
+                use crate::app_test::initialize_tracker_dependencies;
                 use crate::core::{self, statistics};
                 use crate::servers::udp::connection_cookie::make;
                 use crate::servers::udp::handlers::handle_announce;
diff --git a/src/servers/udp/server/mod.rs b/src/servers/udp/server/mod.rs
index 1b0a1da9a..b5da9d326 100644
--- a/src/servers/udp/server/mod.rs
+++ b/src/servers/udp/server/mod.rs
@@ -63,8 +63,8 @@ mod tests {
 
     use super::spawner::Spawner;
     use super::Server;
-    use crate::bootstrap::app::initialize_with_configuration;
-    use crate::core::services::statistics;
+    use crate::bootstrap::app::initialize_global_services;
+    use crate::core::services::{initialize_database, initialize_tracker, initialize_whitelist, statistics};
     use crate::servers::registar::Registar;
     use crate::servers::udp::server::banning::BanService;
     use crate::servers::udp::server::launcher::MAX_CONNECTION_ID_ERRORS_PER_IP;
@@ -76,7 +76,12 @@ mod tests {
         let (stats_event_sender, _stats_repository) = statistics::setup::factory(cfg.core.tracker_usage_statistics);
         let stats_event_sender = Arc::new(stats_event_sender);
         let ban_service = Arc::new(RwLock::new(BanService::new(MAX_CONNECTION_ID_ERRORS_PER_IP)));
-        let tracker = initialize_with_configuration(&cfg);
+
+        initialize_global_services(&cfg);
+
+        let database = initialize_database(&cfg);
+        let whitelist_manager = initialize_whitelist(database.clone());
+        let tracker = Arc::new(initialize_tracker(&cfg, &database, &whitelist_manager));
 
         let udp_trackers = cfg.udp_trackers.clone().expect("missing UDP trackers configuration");
         let config = &udp_trackers[0];
@@ -110,7 +115,12 @@ mod tests {
         let (stats_event_sender, _stats_repository) = statistics::setup::factory(cfg.core.tracker_usage_statistics);
         let stats_event_sender = Arc::new(stats_event_sender);
         let ban_service = Arc::new(RwLock::new(BanService::new(MAX_CONNECTION_ID_ERRORS_PER_IP)));
-        let tracker = initialize_with_configuration(&cfg);
+
+        initialize_global_services(&cfg);
+
+        let database = initialize_database(&cfg);
+        let whitelist_manager = initialize_whitelist(database.clone());
+        let tracker = Arc::new(initialize_tracker(&cfg, &database, &whitelist_manager));
 
         let config = &cfg.udp_trackers.as_ref().unwrap().first().unwrap();
         let bind_to = config.bind_address;
diff --git a/tests/servers/api/environment.rs b/tests/servers/api/environment.rs
index 6658c27da..dcfe526f1 100644
--- a/tests/servers/api/environment.rs
+++ b/tests/servers/api/environment.rs
@@ -6,9 +6,8 @@ use futures::executor::block_on;
 use tokio::sync::RwLock;
 use torrust_tracker_api_client::connection_info::{ConnectionInfo, Origin};
 use torrust_tracker_configuration::{Configuration, HttpApi};
-use torrust_tracker_lib::bootstrap::app::initialize_with_configuration;
+use torrust_tracker_lib::bootstrap::app::{initialize_app_container, initialize_global_services};
 use torrust_tracker_lib::bootstrap::jobs::make_rust_tls;
-use torrust_tracker_lib::core::services::statistics;
 use torrust_tracker_lib::core::statistics::event::sender::Sender;
 use torrust_tracker_lib::core::statistics::repository::Repository;
 use torrust_tracker_lib::core::whitelist::WhiteListManager;
@@ -16,7 +15,6 @@ use torrust_tracker_lib::core::Tracker;
 use torrust_tracker_lib::servers::apis::server::{ApiServer, Launcher, Running, Stopped};
 use torrust_tracker_lib::servers::registar::Registar;
 use torrust_tracker_lib::servers::udp::server::banning::BanService;
-use torrust_tracker_lib::servers::udp::server::launcher::MAX_CONNECTION_ID_ERRORS_PER_IP;
 use torrust_tracker_primitives::peer;
 
 pub struct Environment<S>
@@ -45,15 +43,9 @@ where
 
 impl Environment<Stopped> {
     pub fn new(configuration: &Arc<Configuration>) -> Self {
-        let (stats_event_sender, stats_repository) = statistics::setup::factory(configuration.core.tracker_usage_statistics);
-        let stats_event_sender = Arc::new(stats_event_sender);
-        let stats_repository = Arc::new(stats_repository);
-        let ban_service = Arc::new(RwLock::new(BanService::new(MAX_CONNECTION_ID_ERRORS_PER_IP)));
+        initialize_global_services(configuration);
 
-        let tracker = initialize_with_configuration(configuration);
-
-        // todo: instantiate outside of `initialize_with_configuration`
-        let whitelist_manager = tracker.whitelist_manager.clone();
+        let app_container = initialize_app_container(configuration);
 
         let config = Arc::new(configuration.http_api.clone().expect("missing API configuration"));
 
@@ -65,11 +57,11 @@ impl Environment<Stopped> {
 
         Self {
             config,
-            tracker,
-            stats_event_sender,
-            stats_repository,
-            whitelist_manager,
-            ban_service,
+            tracker: app_container.tracker.clone(),
+            stats_event_sender: app_container.stats_event_sender.clone(),
+            stats_repository: app_container.stats_repository.clone(),
+            whitelist_manager: app_container.whitelist_manager.clone(),
+            ban_service: app_container.ban_service.clone(),
             registar: Registar::default(),
             server,
         }
diff --git a/tests/servers/http/environment.rs b/tests/servers/http/environment.rs
index 845d9d440..131fe4ac1 100644
--- a/tests/servers/http/environment.rs
+++ b/tests/servers/http/environment.rs
@@ -3,9 +3,8 @@ use std::sync::Arc;
 use bittorrent_primitives::info_hash::InfoHash;
 use futures::executor::block_on;
 use torrust_tracker_configuration::{Configuration, HttpTracker};
-use torrust_tracker_lib::bootstrap::app::initialize_with_configuration;
+use torrust_tracker_lib::bootstrap::app::{initialize_app_container, initialize_global_services};
 use torrust_tracker_lib::bootstrap::jobs::make_rust_tls;
-use torrust_tracker_lib::core::services::statistics;
 use torrust_tracker_lib::core::statistics::event::sender::Sender;
 use torrust_tracker_lib::core::statistics::repository::Repository;
 use torrust_tracker_lib::core::whitelist::WhiteListManager;
@@ -34,14 +33,9 @@ impl<S> Environment<S> {
 impl Environment<Stopped> {
     #[allow(dead_code)]
     pub fn new(configuration: &Arc<Configuration>) -> Self {
-        let (stats_event_sender, stats_repository) = statistics::setup::factory(configuration.core.tracker_usage_statistics);
-        let stats_event_sender = Arc::new(stats_event_sender);
-        let stats_repository = Arc::new(stats_repository);
+        initialize_global_services(configuration);
 
-        let tracker = initialize_with_configuration(configuration);
-
-        // todo: instantiate outside of `initialize_with_configuration`
-        let whitelist_manager = tracker.whitelist_manager.clone();
+        let app_container = initialize_app_container(configuration);
 
         let http_tracker = configuration
             .http_trackers
@@ -58,10 +52,10 @@ impl Environment<Stopped> {
 
         Self {
             config,
-            tracker,
-            stats_event_sender,
-            stats_repository,
-            whitelist_manager,
+            tracker: app_container.tracker.clone(),
+            stats_event_sender: app_container.stats_event_sender.clone(),
+            stats_repository: app_container.stats_repository.clone(),
+            whitelist_manager: app_container.whitelist_manager.clone(),
             registar: Registar::default(),
             server,
         }
diff --git a/tests/servers/udp/environment.rs b/tests/servers/udp/environment.rs
index 06a22229e..81e626e1c 100644
--- a/tests/servers/udp/environment.rs
+++ b/tests/servers/udp/environment.rs
@@ -4,14 +4,12 @@ use std::sync::Arc;
 use bittorrent_primitives::info_hash::InfoHash;
 use tokio::sync::RwLock;
 use torrust_tracker_configuration::{Configuration, UdpTracker, DEFAULT_TIMEOUT};
-use torrust_tracker_lib::bootstrap::app::initialize_with_configuration;
-use torrust_tracker_lib::core::services::statistics;
+use torrust_tracker_lib::bootstrap::app::{initialize_app_container, initialize_global_services};
 use torrust_tracker_lib::core::statistics::event::sender::Sender;
 use torrust_tracker_lib::core::statistics::repository::Repository;
 use torrust_tracker_lib::core::Tracker;
 use torrust_tracker_lib::servers::registar::Registar;
 use torrust_tracker_lib::servers::udp::server::banning::BanService;
-use torrust_tracker_lib::servers::udp::server::launcher::MAX_CONNECTION_ID_ERRORS_PER_IP;
 use torrust_tracker_lib::servers::udp::server::spawner::Spawner;
 use torrust_tracker_lib::servers::udp::server::states::{Running, Stopped};
 use torrust_tracker_lib::servers::udp::server::Server;
@@ -44,12 +42,9 @@ where
 impl Environment<Stopped> {
     #[allow(dead_code)]
     pub fn new(configuration: &Arc<Configuration>) -> Self {
-        let (stats_event_sender, stats_repository) = statistics::setup::factory(configuration.core.tracker_usage_statistics);
-        let stats_event_sender = Arc::new(stats_event_sender);
-        let stats_repository = Arc::new(stats_repository);
-        let ban_service = Arc::new(RwLock::new(BanService::new(MAX_CONNECTION_ID_ERRORS_PER_IP)));
+        initialize_global_services(configuration);
 
-        let tracker = initialize_with_configuration(configuration);
+        let app_container = initialize_app_container(configuration);
 
         let udp_tracker = configuration.udp_trackers.clone().expect("missing UDP tracker configuration");
 
@@ -61,10 +56,10 @@ impl Environment<Stopped> {
 
         Self {
             config,
-            tracker,
-            stats_event_sender,
-            stats_repository,
-            ban_service,
+            tracker: app_container.tracker.clone(),
+            stats_event_sender: app_container.stats_event_sender.clone(),
+            stats_repository: app_container.stats_repository.clone(),
+            ban_service: app_container.ban_service.clone(),
             registar: Registar::default(),
             server,
         }