diff --git a/Cargo.lock b/Cargo.lock
index 9d3cd745a..183670639 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1288,7 +1288,9 @@ dependencies = [
  "once_cell",
  "prost-types",
  "regex",
+ "serde",
  "tokio",
+ "toml",
  "tonic",
  "tracing",
  "tracing-journald",
@@ -1342,6 +1344,15 @@ dependencies = [
  "tokio",
 ]
 
+[[package]]
+name = "toml"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "tonic"
 version = "0.6.1"
diff --git a/console.toml b/console.toml
new file mode 100644
index 000000000..b0a663e2a
--- /dev/null
+++ b/console.toml
@@ -0,0 +1,9 @@
+no_colors = false
+lang = "en_us.UTF-8"
+ascii_only = true
+palette = "8"
+
+
+[toggles]
+color_durations = true
+color_terminated = true
diff --git a/tokio-console/Cargo.toml b/tokio-console/Cargo.toml
index 6e2f7347b..fc844795b 100644
--- a/tokio-console/Cargo.toml
+++ b/tokio-console/Cargo.toml
@@ -44,3 +44,5 @@ h2 = "0.3"
 regex = "1.5"
 once_cell = "1.8"
 humantime = "2.1.0"
+serde = { version = "1", features = ["derive"] }
+toml = "0.5"
diff --git a/tokio-console/src/config.rs b/tokio-console/src/config.rs
index 9379642e2..01e7e5d79 100644
--- a/tokio-console/src/config.rs
+++ b/tokio-console/src/config.rs
@@ -1,5 +1,9 @@
 use crate::view::Palette;
 use clap::{ArgGroup, Parser as Clap, ValueHint};
+use serde::{Deserialize, Serialize};
+use std::env;
+use std::fs;
+use std::path::Path;
 use std::process::Command;
 use std::str::FromStr;
 use std::time::Duration;
@@ -28,7 +32,7 @@ pub struct Config {
     #[clap(long = "log", env = "RUST_LOG", default_value = "off")]
     pub(crate) env_filter: tracing_subscriber::EnvFilter,
 
-    #[clap(flatten)]
+    #[clap(skip)]
     pub(crate) view_options: ViewOptions,
 
     /// How long to continue displaying completed tasks and dropped resources
@@ -66,7 +70,7 @@ pub struct Config {
 #[derive(Debug)]
 struct RetainFor(Option<Duration>);
 
-#[derive(Clap, Debug, Clone)]
+#[derive(Clap, Debug, Clone, Default, Deserialize, Serialize)]
 #[clap(group = ArgGroup::new("colors").conflicts_with("no-colors"))]
 pub struct ViewOptions {
     /// Disable ANSI colors entirely.
@@ -107,7 +111,7 @@ pub struct ViewOptions {
 }
 
 /// Toggles on and off color coding for individual UI elements.
-#[derive(Clap, Debug, Copy, Clone)]
+#[derive(Clap, Debug, Copy, Clone, Deserialize, Serialize)]
 pub struct ColorToggles {
     /// Disable color-coding for duration units.
     #[clap(long = "no-duration-colors", parse(from_flag = std::ops::Not::not), group = "colors")]
@@ -118,9 +122,37 @@ pub struct ColorToggles {
     pub(crate) color_terminated: bool,
 }
 
+impl Default for ColorToggles {
+    fn default() -> Self {
+        Self {
+            color_durations: true,
+            color_terminated: true,
+        }
+    }
+}
+
 // === impl Config ===
 
 impl Config {
+    pub fn from_config() -> color_eyre::Result<Self> {
+        let xdg_config_path = env::var_os("XDG_CONFIG_HOME").map(|mut base| {
+            base.push("/tokio-console/console.toml");
+            base
+        });
+        let xdg_view_opt = xdg_config_path.and_then(ViewOptions::from_config);
+        let current_view_opt = ViewOptions::from_config("console.toml");
+
+        let config = Config::parse();
+
+        match xdg_view_opt.or(current_view_opt) {
+            None => Ok(config),
+            Some(view_opt) => Ok(Self {
+                view_options: view_opt,
+                ..config
+            }),
+        }
+    }
+
     pub fn trace_init(&mut self) -> color_eyre::Result<()> {
         let filter = std::mem::take(&mut self.env_filter);
         use tracing_subscriber::prelude::*;
@@ -166,6 +198,16 @@ impl Config {
 // === impl ViewOptions ===
 
 impl ViewOptions {
+    pub(crate) fn from_config<P>(path: P) -> Option<Self>
+    where
+        P: AsRef<Path> + std::fmt::Debug,
+    {
+        match fs::read_to_string(&path) {
+            Err(_) => None,
+            Ok(conf) => toml::from_str(&conf).ok(),
+        }
+    }
+
     pub fn is_utf8(&self) -> bool {
         self.lang.ends_with("UTF-8") && !self.ascii_only
     }
diff --git a/tokio-console/src/main.rs b/tokio-console/src/main.rs
index e41241d7c..190843d3f 100644
--- a/tokio-console/src/main.rs
+++ b/tokio-console/src/main.rs
@@ -2,7 +2,6 @@ use color_eyre::{eyre::eyre, Help, SectionExt};
 use console_api::tasks::TaskDetails;
 use state::State;
 
-use clap::Parser as Clap;
 use futures::stream::StreamExt;
 use tokio::sync::{mpsc, watch};
 use tui::{
@@ -26,7 +25,7 @@ mod warnings;
 
 #[tokio::main]
 async fn main() -> color_eyre::Result<()> {
-    let mut args = config::Config::parse();
+    let mut args = config::Config::from_config()?;
     let retain_for = args.retain_for();
     args.trace_init()?;
     tracing::debug!(?args.target_addr, ?args.view_options);
diff --git a/tokio-console/src/view/styles.rs b/tokio-console/src/view/styles.rs
index 1f183ec0e..069aa38c3 100644
--- a/tokio-console/src/view/styles.rs
+++ b/tokio-console/src/view/styles.rs
@@ -1,4 +1,5 @@
 use crate::config;
+use serde::{Deserialize, Serialize};
 use std::{borrow::Cow, str::FromStr};
 use tui::{
     style::{Color, Modifier, Style},
@@ -12,17 +13,23 @@ pub struct Styles {
     pub(crate) utf8: bool,
 }
 
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+// possible_values = &["8", "16", "256", "all", "off"],
+#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
 #[repr(u8)]
 pub enum Palette {
+    #[serde(rename = "off")]
     NoColors,
     /// Use ANSI 8 color palette only.
+    #[serde(rename = "8")]
     Ansi8,
     /// Use ANSI 16 color palette only.
+    #[serde(rename = "16")]
     Ansi16,
     /// Enable ANSI 256-color palette.
+    #[serde(rename = "256")]
     Ansi256,
     /// Enable all RGB colors.
+    #[serde(rename = "all")]
     All,
 }