Warn if the user edits a [server] config option
diff --git a/lib/streamlit/config.py b/lib/streamlit/config.py
index df1fa38778c0..231e4dc3a790 100644
--- a/lib/streamlit/config.py
+++ b/lib/streamlit/config.py
@@ -33,11 +33,6 @@
 from streamlit import util
 from streamlit.config_option import ConfigOption
 
-# TODO(vincent): Change the Dict type annotation below to OrderedDict once
-# Python 3.7 is required. Python currently gets confused at seeing OrderedDict
-# as a type annotation.
-ConfigOptions = Dict[str, ConfigOption]
-
 # Config System Global State #
 
 # Descriptions of each of the possible config sections.
@@ -56,10 +51,10 @@
 # to `streamlit run`, etc. Note that this and _config_options below are
 # OrderedDicts to ensure stable ordering when printed using
 # `streamlit config show`.
-_config_options_template: ConfigOptions = OrderedDict()
+_config_options_template: Dict[str, ConfigOption] = OrderedDict()
 
 # Stores the current state of config options.
-_config_options: Optional[ConfigOptions] = None
+_config_options: Optional[Dict[str, ConfigOption]] = None
 
 
 # Indicates that a config option was defined by the user.
@@ -224,7 +219,7 @@ def _delete_option(key):
     """
     try:
         del _config_options_template[key]
-        del cast(ConfigOptions, _config_options)[key]
+        del cast(Dict[str, ConfigOption], _config_options)[key]
     except Exception:
         pass
 
@@ -909,11 +904,11 @@ def is_manually_set(option_name):
     )
 
 
-def show_config():
+def show_config() -> None:
     """Print all config options to the terminal."""
     with _config_lock:
         config_util.show_config(
-            _section_descriptions, cast(ConfigOptions, _config_options)
+            _section_descriptions, cast(Dict[str, ConfigOption], _config_options)
         )
 
 
@@ -1031,7 +1026,7 @@ def _maybe_convert_to_number(v):
 
 def get_config_options(
     force_reparse=False, options_from_flags: Optional[Dict[str, Any]] = None
-) -> ConfigOptions:
+) -> Dict[str, ConfigOption]:
     """Create and return a dict mapping config option names to their values,
     returning a cached dict if possible.
 
@@ -1053,7 +1048,7 @@ def get_config_options(
 
     Returns
     ----------
-    ConfigOptions
+    Dict[str, ConfigOption]
         An ordered dict that maps config option names to their values.
     """
     global _config_options
@@ -1072,6 +1067,7 @@ def get_config_options(
         if _config_options and not force_reparse:
             return _config_options
 
+        old_options = _config_options
         _config_options = copy.deepcopy(_config_options_template)
 
         # Values set in files later in the CONFIG_FILENAMES list overwrite those
@@ -1088,6 +1084,18 @@ def get_config_options(
         for opt_name, opt_val in options_from_flags.items():
             _set_option(opt_name, opt_val, _DEFINED_BY_FLAG)
 
+        if old_options and config_util.server_option_changed(
+            old_options, _config_options
+        ):
+            # Import logger locally to prevent circular references.
+            from streamlit.logger import get_logger
+
+            LOGGER = get_logger(__name__)
+            LOGGER.warning(
+                "An update to the [server] config option section was detected."
+                " To have these changes be reflected, please restart streamlit."
+            )
+
         _on_config_parsed.send()
         return _config_options
 
diff --git a/lib/streamlit/config_util.py b/lib/streamlit/config_util.py
index 82b6d9fc09b1..b5df069aad57 100644
--- a/lib/streamlit/config_util.py
+++ b/lib/streamlit/config_util.py
@@ -6,6 +6,23 @@
 from streamlit.config_option import ConfigOption
 
 
+def server_option_changed(
+    old_options: Dict[str, ConfigOption], new_options: Dict[str, ConfigOption]
+) -> bool:
+    """Return True if and only if an option in the server section differs
+    between old_options and new_options."""
+    for opt_name in old_options.keys():
+        if not opt_name.startswith("server"):
+            continue
+
+        old_val = old_options[opt_name].value
+        new_val = new_options[opt_name].value
+        if old_val != new_val:
+            return True
+
+    return False
+
+
 def show_config(
     section_descriptions: Dict[str, str],
     config_options: Dict[str, ConfigOption],
diff --git a/lib/tests/streamlit/config_test.py b/lib/tests/streamlit/config_test.py
index 163a87b545d8..27408c664ec2 100644
--- a/lib/tests/streamlit/config_test.py
+++ b/lib/tests/streamlit/config_test.py
@@ -861,3 +861,38 @@ def test_config_options_removed_on_reparse(self):
 
             self.assertEqual(None, config.get_option("s3.bucket"))
             self.assertEqual("accessKeyId", config.get_option("s3.accessKeyId"))
+
+    @patch("streamlit.logger.get_logger")
+    def test_config_options_warn_on_server_change(self, get_logger):
+        """Test that a warning is logged if a user changes a config file in the
+        server section."""
+
+        global_config_path = "/mock/home/folder/.streamlit/config.toml"
+        makedirs_patch = patch("streamlit.config.os.makedirs")
+        makedirs_patch.return_value = True
+        pathexists_patch = patch("streamlit.config.os.path.exists")
+        pathexists_patch.side_effect = lambda path: path == global_config_path
+        mock_logger = get_logger()
+
+        global_config = """
+        [server]
+        address = "localhost"
+        """
+        open_patch = patch("streamlit.config.open", mock_open(read_data=global_config))
+
+        with open_patch, makedirs_patch, pathexists_patch:
+            config.get_config_options()
+
+        global_config = """
+        [server]
+        address = "streamlit.io"
+        """
+        open_patch = patch("streamlit.config.open", mock_open(read_data=global_config))
+
+        with open_patch, makedirs_patch, pathexists_patch:
+            config.get_config_options(force_reparse=True)
+
+        mock_logger.warning.assert_any_call(
+            "An update to the [server] config option section was detected."
+            " To have these changes be reflected, please restart streamlit."
+        )
diff --git a/lib/tests/streamlit/config_util_test.py b/lib/tests/streamlit/config_util_test.py
index d0319d0547d3..efd43b8b4444 100644
--- a/lib/tests/streamlit/config_util_test.py
+++ b/lib/tests/streamlit/config_util_test.py
@@ -13,10 +13,23 @@
 # limitations under the License.
 
 """Config Util Unittest."""
+import copy
 import textwrap
 import unittest
 
+from parameterized import parameterized
+
 from streamlit import config_util
+from streamlit import config
+
+CONFIG_OPTIONS_TEMPLATE = config._config_options_template
+
+
+def create_config_options(overrides):
+    config_options = copy.deepcopy(CONFIG_OPTIONS_TEMPLATE)
+    for opt_name, opt_val in overrides.items():
+        config_options[opt_name].set_value(opt_val, "test")
+    return config_options
 
 
 class ConfigUtilTest(unittest.TestCase):
@@ -46,3 +59,50 @@ def test_clean_paragraphs(self):
 
         result = config_util._clean_paragraphs(input)
         self.assertEqual(truth, result)
+
+    @parameterized.expand(
+        [
+            # Nothing changed.
+            (
+                {
+                    "s3.secretAccessKey": "shhhhhhh",
+                    "server.address": "localhost",
+                },
+                {
+                    "s3.secretAccessKey": "shhhhhhh",
+                    "server.address": "localhost",
+                },
+                False,
+            ),
+            # A non-server config option changed.
+            (
+                {
+                    "s3.secretAccessKey": "shhhhhhh",
+                    "server.address": "localhost",
+                },
+                {
+                    "s3.secretAccessKey": "SHHHHHHH!!!!!! >:(",
+                    "server.address": "localhost",
+                },
+                False,
+            ),
+            # A server config option changed.
+            (
+                {
+                    "s3.secretAccessKey": "shhhhhhh",
+                    "server.address": "localhost",
+                },
+                {
+                    "s3.secretAccessKey": "shhhhhhh",
+                    "server.address": "streamlit.io",
+                },
+                True,
+            ),
+        ]
+    )
+    def test_server_option_changed(self, old, new, changed):
+        old_options = create_config_options(old)
+        new_options = create_config_options(new)
+        self.assertEqual(
+            config_util.server_option_changed(old_options, new_options), changed
+        )

We can't always automatically apply these without a full server restart,
so we should bug the user to reboot streamlit.
    
Note that warning for all server config options is overly broad, but
it's certainly better to over-warn users than under-warn them in this
case.