diff --git a/README.rst b/README.rst
index 05a183c..09e2e4a 100644
--- a/README.rst
+++ b/README.rst
@@ -1,5 +1,5 @@
 =====
-APRSD  
+APRSD
 =====
 by KM6LYW and WB4BOR
 
diff --git a/aprsd/main.py b/aprsd/main.py
index 119a9a6..08ffda6 100644
--- a/aprsd/main.py
+++ b/aprsd/main.py
@@ -199,6 +199,43 @@ def setup_logging(config, loglevel, quiet):
             imap_logger.addHandler(sh)
 
 
+@main.command()
+@click.option(
+    "--loglevel",
+    default="INFO",
+    show_default=True,
+    type=click.Choice(
+        ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"],
+        case_sensitive=False,
+    ),
+    show_choices=True,
+    help="The log level to use for aprsd.log",
+)
+@click.option(
+    "-c",
+    "--config",
+    "config_file",
+    show_default=True,
+    default=utils.DEFAULT_CONFIG_FILE,
+    help="The aprsd config file to use for options.",
+)
+def check_version(loglevel, config_file):
+    config = utils.parse_config(config_file)
+
+    # Force setting the config to the modules that need it
+    # TODO(Walt): convert these modules to classes that can
+    # Accept the config as a constructor param, instead of this
+    # hacky global setting
+    email.CONFIG = config
+
+    setup_logging(config, loglevel, False)
+    level, msg = utils._check_version()
+    if level:
+        LOG.warning(msg)
+    else:
+        LOG.info(msg)
+
+
 @main.command()
 def sample_config():
     """This dumps the config to stdout."""
@@ -417,6 +454,12 @@ def server(
     email.CONFIG = config
 
     setup_logging(config, loglevel, quiet)
+    level, msg = utils._check_version()
+    if level:
+        LOG.warning(msg)
+    else:
+        LOG.info(msg)
+
     if config["aprsd"].get("trace", False):
         trace.setup_tracing(["method", "api"])
     LOG.info("APRSD Started version: {}".format(aprsd.__version__))
diff --git a/aprsd/threads.py b/aprsd/threads.py
index 7eb28f6..5e95890 100644
--- a/aprsd/threads.py
+++ b/aprsd/threads.py
@@ -67,10 +67,11 @@ class APRSDThread(threading.Thread, metaclass=abc.ABCMeta):
 
 class KeepAliveThread(APRSDThread):
     cntr = 0
+    checker_time = datetime.datetime.now()
 
     def __init__(self):
-        super().__init__("KeepAlive")
         tracemalloc.start()
+        super().__init__("KeepAlive")
 
     def loop(self):
         if self.cntr % 6 == 0:
@@ -102,6 +103,13 @@ class KeepAliveThread(APRSDThread):
                 )
             )
             LOG.debug(keepalive)
+            # Check version every hour
+            delta = now - self.checker_time
+            if delta > datetime.timedelta(hours=1):
+                self.checker_time = now
+                level, msg = utils._check_version()
+                if level:
+                    LOG.warning(msg)
         self.cntr += 1
         time.sleep(10)
         return True
diff --git a/aprsd/utils.py b/aprsd/utils.py
index 5158ee5..afeeecb 100644
--- a/aprsd/utils.py
+++ b/aprsd/utils.py
@@ -8,8 +8,10 @@ from pathlib import Path
 import sys
 import threading
 
+import aprsd
 from aprsd import plugin
 import click
+import update_checker
 import yaml
 
 LOG_LEVELS = {
@@ -364,7 +366,7 @@ def parse_config(config_file):
 
 
 def human_size(bytes, units=None):
-    """ Returns a human readable string representation of bytes """
+    """Returns a human readable string representation of bytes"""
     if not units:
         units = [" bytes", "KB", "MB", "GB", "TB", "PB", "EB"]
     return str(bytes) + units[0] if bytes < 1024 else human_size(bytes >> 10, units[1:])
@@ -375,3 +377,20 @@ def strfdelta(tdelta, fmt="{hours}:{minutes}:{seconds}"):
     d["hours"], rem = divmod(tdelta.seconds, 3600)
     d["minutes"], d["seconds"] = divmod(rem, 60)
     return fmt.format(**d)
+
+
+def _check_version():
+    # check for a newer version
+    try:
+        check = update_checker.UpdateChecker()
+        result = check.check("aprsd", aprsd.__version__)
+        if result:
+            # Looks like there is an updated version.
+            return 1, result
+        else:
+            return 0, "APRSD is up to date"
+    except Exception:
+        # probably can't get in touch with pypi for some reason
+        # Lets put up an error and move on.  We might not
+        # have internet in this aprsd deployment.
+        return 1, "Couldn't check for new version of APRSD"
diff --git a/dev-requirements.txt b/dev-requirements.txt
index e407088..5e0b938 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -12,15 +12,15 @@ appdirs==1.4.4
     #   virtualenv
 attrs==20.3.0
     # via pytest
-babel==2.9.0
+babel==2.9.1
     # via sphinx
-black==20.8b1
+black==21.4b2
     # via -r dev-requirements.in
 bleach==3.3.0
     # via readme-renderer
 certifi==2020.12.5
     # via requests
-cffi==1.14.4
+cffi==1.14.5
     # via cryptography
 cfgv==3.2.0
     # via pre-commit
@@ -32,9 +32,9 @@ click==7.1.2
     #   pip-tools
 colorama==0.4.4
     # via twine
-coverage==5.3.1
+coverage==5.5
     # via pytest-cov
-cryptography==3.3.2
+cryptography==3.4.7
     # via secretstorage
 distlib==0.3.1
     # via virtualenv
@@ -48,19 +48,23 @@ filelock==3.0.12
     #   virtualenv
 flake8-polyfill==1.0.2
     # via pep8-naming
-flake8==3.8.4
+flake8==3.9.1
     # via
     #   -r dev-requirements.in
     #   flake8-polyfill
-identify==2.2.2
+identify==2.2.4
     # via pre-commit
 idna==2.10
     # via requests
 imagesize==1.2.0
     # via sphinx
+importlib-metadata==4.0.1
+    # via
+    #   keyring
+    #   twine
 iniconfig==1.1.1
     # via pytest
-isort==5.7.0
+isort==5.8.0
     # via -r dev-requirements.in
 jeepney==0.6.0
     # via
@@ -68,7 +72,7 @@ jeepney==0.6.0
     #   secretstorage
 jinja2==2.11.3
     # via sphinx
-keyring==21.8.0
+keyring==23.0.1
     # via twine
 markupsafe==1.1.1
     # via jinja2
@@ -78,11 +82,11 @@ mypy-extensions==0.4.3
     # via
     #   black
     #   mypy
-mypy==0.790
+mypy==0.812
     # via -r dev-requirements.in
-nodeenv==1.5.0
+nodeenv==1.6.0
     # via pre-commit
-packaging==20.8
+packaging==20.9
     # via
     #   bleach
     #   pytest
@@ -94,45 +98,45 @@ pep517==0.10.0
     # via pip-tools
 pep8-naming==0.11.1
     # via -r dev-requirements.in
-pip-tools==6.0.1
+pip-tools==6.1.0
     # via -r dev-requirements.in
-pkginfo==1.6.1
+pkginfo==1.7.0
     # via twine
 pluggy==0.13.1
     # via
     #   pytest
     #   tox
-pre-commit==2.11.1
+pre-commit==2.12.1
     # via -r dev-requirements.in
 py==1.10.0
     # via
     #   pytest
     #   tox
-pycodestyle==2.6.0
+pycodestyle==2.7.0
     # via flake8
 pycparser==2.20
     # via cffi
-pyflakes==2.2.0
+pyflakes==2.3.1
     # via flake8
-pygments==2.7.4
+pygments==2.9.0
     # via
     #   readme-renderer
     #   sphinx
 pyparsing==2.4.7
     # via packaging
-pytest-cov==2.10.1
+pytest-cov==2.11.1
     # via -r dev-requirements.in
-pytest==6.2.1
+pytest==6.2.3
     # via
     #   -r dev-requirements.in
     #   pytest-cov
-pytz==2020.5
+pytz==2021.1
     # via babel
 pyyaml==5.4.1
     # via pre-commit
-readme-renderer==28.0
+readme-renderer==29.0
     # via twine
-regex==2020.11.13
+regex==2021.4.4
     # via black
 requests-toolbelt==0.9.1
     # via twine
@@ -143,18 +147,17 @@ requests==2.25.1
     #   twine
 rfc3986==1.4.0
     # via twine
-secretstorage==3.3.0
+secretstorage==3.3.1
     # via keyring
 six==1.15.0
     # via
     #   bleach
-    #   cryptography
     #   readme-renderer
     #   tox
     #   virtualenv
-snowballstemmer==2.0.0
+snowballstemmer==2.1.0
     # via sphinx
-sphinx==3.4.3
+sphinx==3.5.4
     # via -r dev-requirements.in
 sphinxcontrib-applehelp==1.0.2
     # via sphinx
@@ -175,28 +178,26 @@ toml==0.10.2
     #   pre-commit
     #   pytest
     #   tox
-tox==3.21.0
+tox==3.23.0
     # via -r dev-requirements.in
-tqdm==4.55.1
+tqdm==4.60.0
     # via twine
-twine==3.3.0
+twine==3.4.1
     # via -r dev-requirements.in
-typed-ast==1.4.2
-    # via
-    #   black
-    #   mypy
-typing-extensions==3.7.4.3
-    # via
-    #   black
-    #   mypy
+typed-ast==1.4.3
+    # via mypy
+typing-extensions==3.10.0.0
+    # via mypy
 urllib3==1.26.4
     # via requests
-virtualenv==20.4.0
+virtualenv==20.4.4
     # via
     #   pre-commit
     #   tox
 webencodings==0.5.1
     # via bleach
+zipp==3.4.1
+    # via importlib-metadata
 
 # The following packages are considered to be unsafe in a requirements file:
 # pip
diff --git a/requirements.in b/requirements.in
index 9f5a0c3..3a6f437 100644
--- a/requirements.in
+++ b/requirements.in
@@ -9,9 +9,12 @@ opencage
 pluggy
 pbr
 pyyaml
-py3-validate-email
+# Allowing a newer version can lead to a conflict with
+# requests.
+py3-validate-email==0.2.16
 pytz
 requests
 six
 thesmuggler
 yfinance
+update_checker
diff --git a/requirements.txt b/requirements.txt
index 2d8909e..86e7e32 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -10,7 +10,7 @@ backoff==1.10.0
     # via opencage
 certifi==2020.12.5
     # via requests
-cffi==1.14.4
+cffi==1.14.5
     # via cryptography
 chardet==4.0.0
     # via requests
@@ -21,7 +21,7 @@ click==7.1.2
     #   -r requirements.in
     #   click-completion
     #   flask
-cryptography==3.3.2
+cryptography==3.4.7
     # via pyopenssl
 dnspython==2.1.0
     # via py3-validate-email
@@ -29,7 +29,7 @@ filelock==3.0.12
     # via py3-validate-email
 flask-classful==0.14.2
     # via -r requirements.in
-flask-httpauth==4.2.0
+flask-httpauth==4.3.0
     # via -r requirements.in
 flask==1.1.2
     # via
@@ -54,19 +54,19 @@ markupsafe==1.1.1
     # via jinja2
 multitasking==0.0.9
     # via yfinance
-numpy==1.20.1
+numpy==1.20.2
     # via
     #   pandas
     #   yfinance
 opencage==1.2.2
     # via -r requirements.in
-pandas==1.2.2
+pandas==1.2.4
     # via yfinance
-pbr==5.5.1
+pbr==5.6.0
     # via -r requirements.in
 pluggy==0.13.1
     # via -r requirements.in
-py3-validate-email==0.2.12
+py3-validate-email==0.2.16
     # via -r requirements.in
 pycparser==2.20
     # via cffi
@@ -74,7 +74,7 @@ pyopenssl==20.0.1
     # via opencage
 python-dateutil==2.8.1
     # via pandas
-pytz==2020.5
+pytz==2021.1
     # via
     #   -r requirements.in
     #   pandas
@@ -84,23 +84,25 @@ requests==2.25.1
     # via
     #   -r requirements.in
     #   opencage
+    #   update-checker
     #   yfinance
-shellingham==1.3.2
+shellingham==1.4.0
     # via click-completion
 six==1.15.0
     # via
     #   -r requirements.in
     #   click-completion
-    #   cryptography
     #   imapclient
     #   opencage
     #   pyopenssl
     #   python-dateutil
 thesmuggler==1.0.1
     # via -r requirements.in
+update-checker==0.18.0
+    # via -r requirements.in
 urllib3==1.26.4
     # via requests
 werkzeug==1.0.1
     # via flask
-yfinance==0.1.55
+yfinance==0.1.59
     # via -r requirements.in