diff --git a/Cargo.lock b/Cargo.lock
index 611f9026..4d651bd6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -519,12 +519,14 @@ version = "0.1.0"
 dependencies = [
  "borsh",
  "cuprate-constants",
+ "cuprate-helper",
  "cuprate-p2p-core",
  "cuprate-pruning",
  "cuprate-test-utils",
  "futures",
  "indexmap",
  "rand",
+ "serde",
  "thiserror",
  "tokio",
  "tokio-util",
@@ -733,6 +735,7 @@ dependencies = [
  "libc",
  "monero-serai",
  "rayon",
+ "serde",
  "tokio",
  "windows",
 ]
@@ -812,6 +815,7 @@ dependencies = [
  "futures",
  "hex",
  "hex-literal",
+ "serde",
  "thiserror",
  "tokio",
  "tokio-stream",
diff --git a/binaries/cuprated/Cargo.toml b/binaries/cuprated/Cargo.toml
index c8ccd5a6..a2ded303 100644
--- a/binaries/cuprated/Cargo.toml
+++ b/binaries/cuprated/Cargo.toml
@@ -19,10 +19,10 @@ cuprate-fixed-bytes      = { path = "../../net/fixed-bytes" }
 cuprate-levin            = { path = "../../net/levin" }
 cuprate-wire             = { path = "../../net/wire" }
 cuprate-p2p              = { path = "../../p2p/p2p" }
-cuprate-p2p-core         = { path = "../../p2p/p2p-core" }
+cuprate-p2p-core         = { path = "../../p2p/p2p-core", features = ["serde"] }
 cuprate-dandelion-tower  = { path = "../../p2p/dandelion-tower" }
 cuprate-async-buffer     = { path = "../../p2p/async-buffer" }
-cuprate-address-book     = { path = "../../p2p/address-book" }
+cuprate-address-book     = { path = "../../p2p/address-book", features = ["serde_config"] }
 cuprate-blockchain       = { path = "../../storage/blockchain" }
 cuprate-database-service = { path = "../../storage/service" }
 cuprate-txpool           = { path = "../../storage/txpool" }
diff --git a/binaries/cuprated/src/config.rs b/binaries/cuprated/src/config.rs
index d613c1fc..898db414 100644
--- a/binaries/cuprated/src/config.rs
+++ b/binaries/cuprated/src/config.rs
@@ -1 +1,12 @@
 //! cuprated config
+use serde::{Deserialize, Serialize};
+
+mod sections;
+
+use sections::P2PConfig;
+
+#[derive(Default, Deserialize, Serialize)]
+#[serde(deny_unknown_fields, default)]
+pub struct Config {
+    p2p: P2PConfig,
+}
diff --git a/binaries/cuprated/src/config/sections.rs b/binaries/cuprated/src/config/sections.rs
new file mode 100644
index 00000000..c345d229
--- /dev/null
+++ b/binaries/cuprated/src/config/sections.rs
@@ -0,0 +1,44 @@
+use cuprate_address_book::AddressBookConfig;
+use cuprate_p2p_core::ClearNetServerCfg;
+use serde::{Deserialize, Serialize};
+
+#[derive(Default, Deserialize, Serialize)]
+#[serde(deny_unknown_fields, default)]
+pub struct P2PConfig {
+    clear_net: ClearNetConfig,
+}
+
+#[derive(Default, Deserialize, Serialize)]
+#[serde(deny_unknown_fields, default)]
+pub struct ClearNetConfig {
+    server: ClearNetServerCfg,
+    #[serde(flatten)]
+    flattened: SharedNetConfig,
+}
+
+#[derive(Deserialize, Serialize)]
+#[serde(deny_unknown_fields, default)]
+pub struct SharedNetConfig {
+    /// The number of outbound connections to make and try keep.
+    pub outbound_connections: usize,
+    /// The amount of extra connections we can make if we are under load from the rest of Cuprate.
+    pub extra_outbound_connections: usize,
+    /// The maximum amount of inbound connections
+    pub max_inbound_connections: usize,
+    /// port to use to accept p2p connections.
+    pub p2p_port: u16,
+    /// The address book config.
+    pub address_book_config: AddressBookConfig,
+}
+
+impl Default for SharedNetConfig {
+    fn default() -> Self {
+        Self {
+            outbound_connections: 32,
+            extra_outbound_connections: 8,
+            max_inbound_connections: 128,
+            p2p_port: 18080,
+            address_book_config: AddressBookConfig::default(),
+        }
+    }
+}
diff --git a/helper/Cargo.toml b/helper/Cargo.toml
index 111c6f02..71d12c0e 100644
--- a/helper/Cargo.toml
+++ b/helper/Cargo.toml
@@ -33,6 +33,8 @@ futures      = { workspace = true, optional = true, features = ["std"] }
 monero-serai = { workspace = true, optional = true }
 rayon        = { workspace = true, optional = true }
 
+serde        = { workspace = true, optional = true, features = ["derive"] }
+
 # This is kinda a stupid work around.
 # [thread] needs to activate one of these libs (windows|libc)
 # although it depends on what target we're building for.
diff --git a/helper/src/network.rs b/helper/src/network.rs
index f3224b33..02574752 100644
--- a/helper/src/network.rs
+++ b/helper/src/network.rs
@@ -5,6 +5,7 @@
 //! into it's own crate.
 //!
 //! `#[no_std]` compatible.
+// TODO: move to types crate.
 
 const MAINNET_NETWORK_ID: [u8; 16] = [
     0x12, 0x30, 0xF1, 0x71, 0x61, 0x04, 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x10,
@@ -18,6 +19,7 @@ const STAGENET_NETWORK_ID: [u8; 16] = [
 
 /// An enum representing every Monero network.
 #[derive(Debug, Clone, Copy, Default)]
+#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
 pub enum Network {
     /// Mainnet
     #[default]
diff --git a/p2p/address-book/Cargo.toml b/p2p/address-book/Cargo.toml
index 9afc2552..79fe9211 100644
--- a/p2p/address-book/Cargo.toml
+++ b/p2p/address-book/Cargo.toml
@@ -5,9 +5,13 @@ edition = "2021"
 license = "MIT"
 authors = ["Boog900"]
 
+[features]
+defualt = []
+serde_config = ["dep:serde", "dep:cuprate-helper"]
 
 [dependencies]
 cuprate-constants = { path = "../../constants" }
+cuprate-helper = { path = "../../helper", features = ["std", "fs"], optional = true }
 cuprate-pruning = { path = "../../pruning" }
 cuprate-p2p-core = { path = "../p2p-core" }
 
@@ -23,7 +27,8 @@ indexmap = { workspace = true, features = ["std"] }
 
 rand = { workspace = true, features = ["std", "std_rng"] }
 
-borsh = { workspace = true, features = ["derive", "std"]}
+borsh = { workspace = true, features = ["derive", "std"] }
+serde = { workspace = true, features = ["derive", "std"], optional = true }
 
 [dev-dependencies]
 cuprate-test-utils = {path = "../../test-utils"}
diff --git a/p2p/address-book/src/lib.rs b/p2p/address-book/src/lib.rs
index c0903485..2d7b3d63 100644
--- a/p2p/address-book/src/lib.rs
+++ b/p2p/address-book/src/lib.rs
@@ -20,6 +20,8 @@ mod store;
 
 /// The address book config.
 #[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde_config", derive(serde::Deserialize, serde::Serialize))]
+#[cfg_attr(feature = "serde_config", serde(deny_unknown_fields, default))]
 pub struct AddressBookConfig {
     /// The maximum number of white peers in the peer list.
     ///
@@ -29,12 +31,24 @@ pub struct AddressBookConfig {
     ///
     /// Gray peers are peers we are yet to make a connection to.
     pub max_gray_list_length: usize,
-    /// The location to store the address book.
-    pub peer_store_file: PathBuf,
+    /// The location to store the peer store file.
+    pub peer_store_folder: PathBuf,
     /// The amount of time between saving the address book to disk.
     pub peer_save_period: Duration,
 }
 
+#[cfg(feature = "serde_config")]
+impl Default for AddressBookConfig {
+    fn default() -> Self {
+        Self {
+            max_white_list_length: 1000,
+            max_gray_list_length: 5000,
+            peer_store_folder: cuprate_helper::fs::CUPRATE_CACHE_DIR.clone(),
+            peer_save_period: Duration::from_secs(90),
+        }
+    }
+}
+
 /// Possible errors when dealing with the address book.
 /// This is boxed when returning an error in the [`tower::Service`].
 #[derive(Debug, thiserror::Error, Eq, PartialEq)]
@@ -63,11 +77,6 @@ pub enum AddressBookError {
 pub async fn init_address_book<Z: BorshNetworkZone>(
     cfg: AddressBookConfig,
 ) -> Result<book::AddressBook<Z>, std::io::Error> {
-    tracing::info!(
-        "Loading peers from file: {} ",
-        cfg.peer_store_file.display()
-    );
-
     let (white_list, gray_list) = match store::read_peers_from_disk::<Z>(&cfg).await {
         Ok(res) => res,
         Err(e) if e.kind() == ErrorKind::NotFound => (vec![], vec![]),
diff --git a/p2p/address-book/src/store.rs b/p2p/address-book/src/store.rs
index 07c117e1..91309182 100644
--- a/p2p/address-book/src/store.rs
+++ b/p2p/address-book/src/store.rs
@@ -39,7 +39,7 @@ pub(crate) fn save_peers_to_disk<Z: BorshNetworkZone>(
     })
     .unwrap();
 
-    let file = cfg.peer_store_file.clone();
+    let file = cfg.peer_store_folder.join(format!("{}_p2p_state", Z::NAME));
     spawn_blocking(move || fs::write(&file, &data))
 }
 
@@ -52,7 +52,10 @@ pub(crate) async fn read_peers_from_disk<Z: BorshNetworkZone>(
     ),
     std::io::Error,
 > {
-    let file = cfg.peer_store_file.clone();
+    let file = cfg.peer_store_folder.join(format!("{}_p2p_state", Z::NAME));
+
+    tracing::info!("Loading peers from file: {} ", file.display());
+
     let data = spawn_blocking(move || fs::read(file)).await.unwrap()?;
 
     let de_ser: DeserPeerDataV1<Z::Addr> = from_slice(&data)?;
diff --git a/p2p/p2p-core/Cargo.toml b/p2p/p2p-core/Cargo.toml
index a30590fa..0a4b781f 100644
--- a/p2p/p2p-core/Cargo.toml
+++ b/p2p/p2p-core/Cargo.toml
@@ -27,6 +27,7 @@ tracing = { workspace = true, features = ["std", "attributes"] }
 hex-literal = { workspace = true }
 
 borsh = { workspace = true, features = ["derive", "std"], optional = true }
+serde = { workspace = true, features = ["derive", "std"], optional = true }
 
 [dev-dependencies]
 cuprate-test-utils = { path = "../../test-utils" }
diff --git a/p2p/p2p-core/src/network_zones/clear.rs b/p2p/p2p-core/src/network_zones/clear.rs
index 261d5ad3..3c50f315 100644
--- a/p2p/p2p-core/src/network_zones/clear.rs
+++ b/p2p/p2p-core/src/network_zones/clear.rs
@@ -38,10 +38,20 @@ impl NetZoneAddress for SocketAddr {
 }
 
 #[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
+#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
 pub struct ClearNetServerCfg {
     pub ip: IpAddr,
 }
 
+impl Default for ClearNetServerCfg {
+    fn default() -> Self {
+        Self {
+            ip: IpAddr::V4(Ipv4Addr::UNSPECIFIED),
+        }
+    }
+}
+
 #[derive(Clone, Copy)]
 pub enum ClearNet {}