diff --git a/ansible/group_vars/all/seed b/ansible/group_vars/all/seed
index c4cd6c274..4df06f3b5 100644
--- a/ansible/group_vars/all/seed
+++ b/ansible/group_vars/all/seed
@@ -99,3 +99,9 @@ seed_sysctl_parameters: {}
 # List of users to create. This should be in a format accepted by the
 # singleplatform-eng.users role.
 seed_users: "{{ users_default }}"
+
+###############################################################################
+# Seed node additional containers configuration
+
+# List of additional containers to deploy
+seed_containers: {}
diff --git a/ansible/roles/deploy-containers/defaults/main.yml b/ansible/roles/deploy-containers/defaults/main.yml
new file mode 100644
index 000000000..84c77a7e4
--- /dev/null
+++ b/ansible/roles/deploy-containers/defaults/main.yml
@@ -0,0 +1,13 @@
+---
+deploy_containers_defaults:
+  comparisons:
+    image: strict
+    env: strict
+    volumes: strict
+  detach: True
+  network_mode: "host"
+  init: True
+  privileged: False
+  restart_policy: "unless-stopped"
+
+deploy_containers_docker_api_timeout: 120
diff --git a/ansible/roles/deploy-containers/tasks/deploy.yml b/ansible/roles/deploy-containers/tasks/deploy.yml
new file mode 100644
index 000000000..64635e7cb
--- /dev/null
+++ b/ansible/roles/deploy-containers/tasks/deploy.yml
@@ -0,0 +1,36 @@
+---
+- name: "[{{ container_name }}] Ensure we have latest image"
+  docker_image:
+    name: "{{ container_config.image }}"
+    tag: "{{ container_config.tag | default(omit) }}"
+    source: pull
+
+- name: "[{{ container_name }}] Include tasks file for pre task(s)"
+  include_tasks: "{{ container_config.pre }}"
+  when: container_config.pre is defined
+
+- name: "[{{ container_name }}] Start container"
+  docker_container:
+    capabilities: "{{ container_config.capabilities | default(omit) }}"
+    command: "{{ container_config.command | default(omit) }}"
+    comparisons: "{{ container_config.comparisons | default(deploy_containers_defaults.comparisons) }}"
+    detach: "{{ container_config.detach | default(deploy_containers_defaults.detach) }}"
+    env: "{{ container_config.env | default(omit) }}"
+    name: "{{ container_name }}"
+    network_mode: "{{ container_config.network_mode | default(deploy_containers_defaults.network_mode) }}"
+    image: "{{ container_config.image }}:{{ container_config.tag | default('latest') }}"
+    init: "{{ container_config.init | default(deploy_containers_defaults.init) }}"
+    ipc_mode: "{{ container_config.ipc_mode | default(omit) }}"
+    pid_mode: "{{ container_config.pid_mode | default(omit) }}"
+    ports: "{{ container_config.ports | default(omit) }}"
+    privileged: "{{ container_config.privileged | default(omit) }}"
+    restart_policy: "{{ container_config.restart_policy | default(deploy_containers_defaults.restart_policy) }}"
+    sysctls: "{{ container_config.sysctls | default(omit) }}"
+    timeout: "{{ deploy_containers_docker_api_timeout }}"
+    ulimits: "{{ container_config.ulimits | default(omit) }}"
+    user: "{{ container_config.user | default(omit) }}"
+    volumes: "{{ container_config.volumes | default(omit) }}"
+
+- name: "[{{ container_name }}] Include tasks file for post task(s)"
+  include_tasks: "{{ container_config.post }}"
+  when: container_config.post is defined
diff --git a/ansible/roles/deploy-containers/tasks/main.yml b/ansible/roles/deploy-containers/tasks/main.yml
new file mode 100644
index 000000000..9d3e15209
--- /dev/null
+++ b/ansible/roles/deploy-containers/tasks/main.yml
@@ -0,0 +1,7 @@
+---
+- name: Deploy containers (loop)
+  include_tasks: deploy.yml
+  vars:
+    container_name: "{{ item.key }}"
+    container_config: "{{ item.value }}"
+  with_dict: "{{ seed_containers }}"
diff --git a/ansible/seed-deploy-containers.yml b/ansible/seed-deploy-containers.yml
new file mode 100644
index 000000000..d32227b28
--- /dev/null
+++ b/ansible/seed-deploy-containers.yml
@@ -0,0 +1,7 @@
+---
+- name: Ensure defined container images are deployed on Seed node
+  hosts: seed
+  tags:
+    - seed-deploy-containers
+  roles:
+    - role: deploy-containers
diff --git a/doc/source/configuration/index.rst b/doc/source/configuration/index.rst
index aeff20398..89e58a4ef 100644
--- a/doc/source/configuration/index.rst
+++ b/doc/source/configuration/index.rst
@@ -14,4 +14,5 @@ Configuration Guide
    kolla-ansible
    bifrost
    ironic-python-agent
+   seed-custom-containers
    nova-cells
diff --git a/doc/source/configuration/seed-custom-containers.rst b/doc/source/configuration/seed-custom-containers.rst
new file mode 100644
index 000000000..ed2a42ac8
--- /dev/null
+++ b/doc/source/configuration/seed-custom-containers.rst
@@ -0,0 +1,61 @@
+.. _configuration-seed-custom-containers:
+
+======================
+Seed custom containers
+======================
+
+This section covers configuration of the user-defined containers deployment
+functionality that runs on the seed host.
+
+Configuration
+=============
+
+For example, to deploy a squid container image:
+
+.. code-block:: yaml
+   :caption: ``seed.yml``
+
+   seed_containers:
+     squid:
+       image: "stackhpc/squid:3.5.20-1"
+       pre: "{{ kayobe_config_path }}/containers/squid/pre.yml"
+       post: "{{ kayobe_config_path }}/containers/squid/post.yml"
+
+Please notice the *optional* pre and post Ansible task files - those need to
+be created in ``kayobe-config`` path and will be run before and after
+particular container deployment.
+
+Possible options for container deployment:
+
+.. code-block:: yaml
+
+   seed_containers:
+     containerA:
+       capabilities:
+       command:
+       comparisons:
+       detach:
+       env:
+       network_mode:
+       image:
+       init:
+       ipc_mode:
+       pid_mode:
+       ports:
+       privileged:
+       restart_policy:
+       sysctls:
+       tag:
+       ulimits:
+       user:
+       volumes:
+
+For a detailed explanation of each option - please see `Ansible
+docker_container <https://docs.ansible.com/ansible/latest/modules/docker_container_module.html>`_
+module page.
+
+List of Kayobe applied defaults to required docker_container variables:
+
+.. literalinclude:: ../../../ansible/roles/deploy-containers/defaults/main.yml
+    :language: yaml
+
diff --git a/doc/source/deployment.rst b/doc/source/deployment.rst
index 413fc09e8..3a352baa2 100644
--- a/doc/source/deployment.rst
+++ b/doc/source/deployment.rst
@@ -182,7 +182,9 @@ After this command has completed the seed services will be active.
    <configuration-kolla-ansible>`. See :ref:`here <configuration-bifrost>` for
    information about configuring Bifrost.
    :ref:`configuration-bifrost-overcloud-root-image` provides information on
-   configuring the root disk image build process.
+   configuring the root disk image build process. See :ref:`here
+   <configuration-seed-custom-containers>` for information about deploying
+   additional, custom containers on seed node.
 
 Building Deployment Images
 --------------------------
diff --git a/etc/kayobe/seed.yml b/etc/kayobe/seed.yml
index 3326c2429..d71544e12 100644
--- a/etc/kayobe/seed.yml
+++ b/etc/kayobe/seed.yml
@@ -76,6 +76,20 @@
 # singleplatform-eng.users role.
 #seed_users:
 
+###############################################################################
+# Seed node containers configuration
+#
+# Dict of container images to start
+# Example:
+# seed_containers:
+#   squid:
+#     name: "squid"
+#     image: "stackhpc/squid:3.5.20-1"
+#     pre: "{{ kayobe_config_path }}/containers/squid/pre.yml"
+#     post: "{{ kayobe_config_path }}/containers/squid/post.yml"
+#
+#seed_containers:
+
 ###############################################################################
 # Dummy variable to allow Ansible to accept this file.
 workaround_ansible_issue_8743: yes
diff --git a/kayobe/cli/commands.py b/kayobe/cli/commands.py
index a02619ebb..2d84969f0 100644
--- a/kayobe/cli/commands.py
+++ b/kayobe/cli/commands.py
@@ -689,6 +689,7 @@ class SeedServiceDeploy(KollaAnsibleMixin, KayobeAnsibleMixin, VaultMixin,
                         Command):
     """Deploy the seed services.
 
+    * Deploys user defined containers
     * Configures kolla-ansible.
     * Configures the bifrost service.
     * Deploys the bifrost container using kolla-ansible.
@@ -702,6 +703,9 @@ class SeedServiceDeploy(KollaAnsibleMixin, KayobeAnsibleMixin, VaultMixin,
 
     def take_action(self, parsed_args):
         self.app.LOG.debug("Deploying seed services")
+        playbooks = _build_playbook_list(
+            "seed-deploy-containers")
+        self.run_kayobe_playbooks(parsed_args, playbooks)
         self.generate_kolla_ansible_config(parsed_args, service_config=False,
                                            bifrost_config=True)
 
@@ -717,6 +721,7 @@ class SeedServiceUpgrade(KollaAnsibleMixin, KayobeAnsibleMixin, VaultMixin,
                          Command):
     """Upgrade the seed services.
 
+    * Deploys user defined containers
     * Configures kolla-ansible.
     * Configures the bifrost service.
     * Prepares the bifrost service for an upgrade.
@@ -731,6 +736,9 @@ class SeedServiceUpgrade(KollaAnsibleMixin, KayobeAnsibleMixin, VaultMixin,
 
     def take_action(self, parsed_args):
         self.app.LOG.debug("Upgrading seed services")
+        playbooks = _build_playbook_list(
+            "seed-deploy-containers")
+        self.run_kayobe_playbooks(parsed_args, playbooks)
         self.generate_kolla_ansible_config(parsed_args, service_config=False,
                                            bifrost_config=True)
 
diff --git a/kayobe/tests/unit/cli/test_commands.py b/kayobe/tests/unit/cli/test_commands.py
index 66452adda..1948caa24 100644
--- a/kayobe/tests/unit/cli/test_commands.py
+++ b/kayobe/tests/unit/cli/test_commands.py
@@ -780,6 +780,10 @@ class TestCase(unittest.TestCase):
         self.assertEqual(0, result)
 
         expected_calls = [
+            mock.call(
+                mock.ANY,
+                [utils.get_data_files_path("ansible", "seed-deploy-containers.yml")],  # noqa
+            ),
             mock.call(
                 mock.ANY,
                 [utils.get_data_files_path("ansible", "kolla-ansible.yml")],
@@ -826,6 +830,10 @@ class TestCase(unittest.TestCase):
         self.assertEqual(0, result)
 
         expected_calls = [
+            mock.call(
+                mock.ANY,
+                [utils.get_data_files_path("ansible", "seed-deploy-containers.yml")],  # noqa
+            ),
             mock.call(
                 mock.ANY,
                 [utils.get_data_files_path("ansible", "kolla-ansible.yml")],
diff --git a/releasenotes/notes/deploy_containers-4199127a91f29be0.yaml b/releasenotes/notes/deploy_containers-4199127a91f29be0.yaml
new file mode 100644
index 000000000..931e25487
--- /dev/null
+++ b/releasenotes/notes/deploy_containers-4199127a91f29be0.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    New feature to deploy user-defined containers on seed node with pre and
+    post scripts has been added to Kayobe.