diff --git a/k8s/beat.yaml b/k8s/beat.yaml
new file mode 100644
index 000000000..7099fec76
--- /dev/null
+++ b/k8s/beat.yaml
@@ -0,0 +1,61 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: beat
+spec:
+  replicas: 1
+  revisionHistoryLimit: 2
+  selector:
+    matchLabels:
+      app: beat
+  strategy:
+    type: Recreate
+  template:
+    metadata:
+      labels:
+        app: beat
+    spec:
+      securityContext:
+        runAsNonRoot: true
+      containers:
+        - name: beat
+          image: "ghcr.io/ietf-tools/datatracker:$APP_IMAGE_TAG"
+          imagePullPolicy: Always
+          ports:
+            - containerPort: 8000
+              name: http
+              protocol: TCP
+          volumeMounts:
+            - name: dt-vol
+              mountPath: /a
+            - name: dt-tmp
+              mountPath: /tmp
+            - name: dt-cfg
+              mountPath: /workspace/ietf/settings_local.py
+              subPath: settings_local.py
+          env:
+            - name: "CONTAINER_ROLE"
+              value: "beat"
+          envFrom:
+            - configMapRef:
+                name: django-config
+          securityContext:
+            allowPrivilegeEscalation: false
+            capabilities:
+              drop:
+              - ALL
+            readOnlyRootFilesystem: true
+            runAsUser: 1000
+            runAsGroup: 1000
+      volumes:
+        # To be overriden with the actual shared volume
+        - name: dt-vol
+        - name: dt-tmp
+          emptyDir:
+            sizeLimit: "2Gi"
+        - name: dt-cfg
+          configMap:
+            name: files-cfgmap
+      dnsPolicy: ClusterFirst
+      restartPolicy: Always
+      terminationGracePeriodSeconds: 30
\ No newline at end of file
diff --git a/k8s/celery.yaml b/k8s/celery.yaml
new file mode 100644
index 000000000..0c6bbf475
--- /dev/null
+++ b/k8s/celery.yaml
@@ -0,0 +1,80 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: celery
+spec:
+  replicas: 1
+  revisionHistoryLimit: 2
+  selector:
+    matchLabels:
+      app: celery
+  strategy:
+    type: Recreate
+  template:
+    metadata:
+      labels:
+        app: celery
+    spec:
+      securityContext:
+        runAsNonRoot: true
+      containers:
+        # -----------------------------------------------------
+        # ScoutAPM Container
+        # -----------------------------------------------------
+        - name: scoutapm
+          image: "scoutapp/scoutapm:version-1.4.0"
+          imagePullPolicy: IfNotPresent
+          livenessProbe:
+            exec:
+              command:
+                - "sh"
+                - "-c"
+                - "./core-agent probe --tcp 0.0.0.0:6590 | grep -q 'Agent found'"
+          securityContext:
+            readOnlyRootFilesystem: true
+            runAsUser: 65534 # "nobody" user by default
+            runAsGroup: 65534  # "nogroup" group by default
+        # -----------------------------------------------------
+        # Celery Container
+        # -----------------------------------------------------
+        - name: celery
+          image: "ghcr.io/ietf-tools/datatracker:$APP_IMAGE_TAG"
+          imagePullPolicy: Always
+          ports:
+            - containerPort: 8000
+              name: http
+              protocol: TCP
+          volumeMounts:
+            - name: dt-vol
+              mountPath: /a
+            - name: dt-tmp
+              mountPath: /tmp
+            - name: dt-cfg
+              mountPath: /workspace/ietf/settings_local.py
+              subPath: settings_local.py
+          env:
+            - name: "CONTAINER_ROLE"
+              value: "celery"
+          envFrom:
+            - configMapRef:
+                name: django-config
+          securityContext:
+            allowPrivilegeEscalation: false
+            capabilities:
+              drop:
+              - ALL
+            readOnlyRootFilesystem: true
+            runAsUser: 1000
+            runAsGroup: 1000
+      volumes:
+        # To be overriden with the actual shared volume
+        - name: dt-vol
+        - name: dt-tmp
+          emptyDir:
+            sizeLimit: "2Gi"
+        - name: dt-cfg
+          configMap:
+            name: files-cfgmap
+      dnsPolicy: ClusterFirst
+      restartPolicy: Always
+      terminationGracePeriodSeconds: 30
\ No newline at end of file
diff --git a/k8s/datatracker.yaml b/k8s/datatracker.yaml
index f63a7004d..6a832fb8b 100644
--- a/k8s/datatracker.yaml
+++ b/k8s/datatracker.yaml
@@ -52,6 +52,9 @@ spec:
             - name: dt-cfg
               mountPath: /workspace/ietf/settings_local.py
               subPath: settings_local.py
+          env:
+            - name: "CONTAINER_ROLE"
+              value: "datatracker"
           envFrom:
             - configMapRef:
                 name: django-config
@@ -77,84 +80,6 @@ spec:
       terminationGracePeriodSeconds: 30
 ---
 apiVersion: v1
-kind: ConfigMap
-metadata:
-  name: django-config
-data:
-    # n.b., these are debug values / non-secret secrets
-  DATATRACKER_SERVER_MODE: "development"  # development for staging, production for production
-  DATATRACKER_ADMINS: |-
-    Robert Sparks <rjsparks@nostrum.com>
-    Ryan Cross <rcross@amsl.com>
-    Kesara Rathnayake <kesara@staff.ietf.org>
-    Jennifer Richards <jennifer@staff.ietf.org>
-    Nicolas Giard <nick@staff.ietf.org>
-  DATATRACKER_ALLOWED_HOSTS: ".ietf.org"  # newline-separated list also allowed
-  # DATATRACKER_DATATRACKER_DEBUG: "false"
-  
-  # DB access details - needs to be filled in
-  # DATATRACKER_DBHOST: "db"
-  # DATATRACKER_DBPORT: "5432"
-  # DATATRACKER_DBNAME: "datatracker"
-  # DATATRACKER_DBUSER: "django"  # secret
-  # DATATRACKER_DBPASS: "RkTkDPFnKpko"  # secret
-  
-  DATATRACKER_DJANGO_SECRET_KEY: "PDwXboUq!=hPjnrtG2=ge#N$Dwy+wn@uivrugwpic8mxyPfHk"  # secret
-
-  # Set this to point testing / staging at the production statics server until we
-  # sort that out
-  # DATATRACKER_STATIC_URL: "https://static.ietf.org/dt/12.10.0/"
-  
-  # DATATRACKER_EMAIL_DEBUG: "true"
-  
-  # Outgoing email details
-  # DATATRACKER_EMAIL_HOST: "localhost"  # defaults to localhost
-  # DATATRACKER_EMAIL_PORT: "2025"  # defaults to 2025
-
-  # The value here is the default from settings.py (i.e., not actually secret)
-  DATATRACKER_NOMCOM_APP_SECRET_B64: "m9pzMezVoFNJfsvU9XSZxGnXnwup6P5ZgCQeEnROOoQ="  # secret
-
-  DATATRACKER_IANA_SYNC_PASSWORD: "this-is-the-iana-sync-password"  # secret
-  DATATRACKER_RFC_EDITOR_SYNC_PASSWORD: "this-is-the-rfc-editor-sync-password"  # secret
-  DATATRACKER_YOUTUBE_API_KEY: "this-is-the-youtube-api-key"  # secret
-  DATATRACKER_GITHUB_BACKUP_API_KEY: "this-is-the-github-backup-api-key"  # secret
-
-  # API key configuration
-  DATATRACKER_API_KEY_TYPE: "ES265"
-  # secret - value here is the default from settings.py (i.e., not actually secret)
-  DATATRACKER_API_PUBLIC_KEY_PEM_B64: |-
-    Ci0tLS0tQkVHSU4gUFVCTElDIEtFWS0tLS0tCk1Ga3dFd1lIS29aSXpqMENBUVlJS
-    29aSXpqMERBUWNEUWdBRXFWb2pzYW9mREpTY3VNSk4rdHNodW15Tk01TUUKZ2Fyel
-    ZQcWtWb3ZtRjZ5RTdJSi9kdjRGY1YrUUtDdEovck9TOGUzNlk4WkFFVll1dWtoZXM
-    weVoxdz09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=
-  # secret - value here is the default from settings.py (i.e., not actually secret)
-  DATATRACKER_API_PRIVATE_KEY_PEM_B64: |- 
-    Ci0tLS0tQkVHSU4gUFJJVkFURSBLRVktLS0tLQpNSUdIQWdFQU1CTUdCeXFHU000O
-    UFnRUdDQ3FHU000OUF3RUhCRzB3YXdJQkFRUWdvSTZMSmtvcEtxOFhySGk5ClFxR1
-    F2RTRBODNURllqcUx6KzhnVUxZZWNzcWhSQU5DQUFTcFdpT3hxaDhNbEp5NHdrMzY
-    yeUc2Ykkwemt3U0IKcXZOVStxUldpK1lYcklUc2duOTIvZ1Z4WDVBb0swbitzNUx4
-    N2ZwanhrQVJWaTY2U0Y2elRKblgKLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=
-
-  # DATATRACKER_MEETECHO_API_BASE: "https://meetings.conf.meetecho.com/api/v1/"
-  DATATRACKER_MEETECHO_CLIENT_ID: "this-is-the-meetecho-client-id"  # secret
-  DATATRACKER_MEETECHO_CLIENT_SECRET: "this-is-the-meetecho-client-secret"  # secret
-
-  # DATATRACKER_MATOMO_SITE_ID: "7"  # must be present to enable Matomo
-  # DATATRACKER_MATOMO_DOMAIN_PATH: "analytics.ietf.org"
-
-  CELERY_PASSWORD: "this-is-a-secret"  # secret
-
-  DATATRACKER_APP_API_TOKENS_JSON: "{}"  # secret 
-
-  # use this to override default - one entry per line
-  # DATATRACKER_CSRF_TRUSTED_ORIGINS: |-
-  #   https://datatracker.staging.ietf.org
-
-  # Scout configuration
-  DATATRACKER_SCOUT_KEY: "this-is-the-scout-key"
-  DATATRACKER_SCOUT_NAME: "StagingDatatracker"
----
-apiVersion: v1
 kind: Service
 metadata:
   name: datatracker
diff --git a/k8s/django-config.yaml b/k8s/django-config.yaml
new file mode 100644
index 000000000..1e27a32f0
--- /dev/null
+++ b/k8s/django-config.yaml
@@ -0,0 +1,77 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: django-config
+data:
+    # n.b., these are debug values / non-secret secrets
+  DATATRACKER_SERVER_MODE: "development"  # development for staging, production for production
+  DATATRACKER_ADMINS: |-
+    Robert Sparks <rjsparks@nostrum.com>
+    Ryan Cross <rcross@amsl.com>
+    Kesara Rathnayake <kesara@staff.ietf.org>
+    Jennifer Richards <jennifer@staff.ietf.org>
+    Nicolas Giard <nick@staff.ietf.org>
+  DATATRACKER_ALLOWED_HOSTS: ".ietf.org"  # newline-separated list also allowed
+  # DATATRACKER_DATATRACKER_DEBUG: "false"
+  
+  # DB access details - needs to be filled in
+  # DATATRACKER_DBHOST: "db"
+  # DATATRACKER_DBPORT: "5432"
+  # DATATRACKER_DBNAME: "datatracker"
+  # DATATRACKER_DBUSER: "django"  # secret
+  # DATATRACKER_DBPASS: "RkTkDPFnKpko"  # secret
+  
+  DATATRACKER_DJANGO_SECRET_KEY: "PDwXboUq!=hPjnrtG2=ge#N$Dwy+wn@uivrugwpic8mxyPfHk"  # secret
+
+  # Set this to point testing / staging at the production statics server until we
+  # sort that out
+  # DATATRACKER_STATIC_URL: "https://static.ietf.org/dt/12.10.0/"
+  
+  # DATATRACKER_EMAIL_DEBUG: "true"
+  
+  # Outgoing email details
+  # DATATRACKER_EMAIL_HOST: "localhost"  # defaults to localhost
+  # DATATRACKER_EMAIL_PORT: "2025"  # defaults to 2025
+
+  # The value here is the default from settings.py (i.e., not actually secret)
+  DATATRACKER_NOMCOM_APP_SECRET_B64: "m9pzMezVoFNJfsvU9XSZxGnXnwup6P5ZgCQeEnROOoQ="  # secret
+
+  DATATRACKER_IANA_SYNC_PASSWORD: "this-is-the-iana-sync-password"  # secret
+  DATATRACKER_RFC_EDITOR_SYNC_PASSWORD: "this-is-the-rfc-editor-sync-password"  # secret
+  DATATRACKER_YOUTUBE_API_KEY: "this-is-the-youtube-api-key"  # secret
+  DATATRACKER_GITHUB_BACKUP_API_KEY: "this-is-the-github-backup-api-key"  # secret
+
+  # API key configuration
+  DATATRACKER_API_KEY_TYPE: "ES265"
+  # secret - value here is the default from settings.py (i.e., not actually secret)
+  DATATRACKER_API_PUBLIC_KEY_PEM_B64: |-
+    Ci0tLS0tQkVHSU4gUFVCTElDIEtFWS0tLS0tCk1Ga3dFd1lIS29aSXpqMENBUVlJS
+    29aSXpqMERBUWNEUWdBRXFWb2pzYW9mREpTY3VNSk4rdHNodW15Tk01TUUKZ2Fyel
+    ZQcWtWb3ZtRjZ5RTdJSi9kdjRGY1YrUUtDdEovck9TOGUzNlk4WkFFVll1dWtoZXM
+    weVoxdz09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=
+  # secret - value here is the default from settings.py (i.e., not actually secret)
+  DATATRACKER_API_PRIVATE_KEY_PEM_B64: |- 
+    Ci0tLS0tQkVHSU4gUFJJVkFURSBLRVktLS0tLQpNSUdIQWdFQU1CTUdCeXFHU000O
+    UFnRUdDQ3FHU000OUF3RUhCRzB3YXdJQkFRUWdvSTZMSmtvcEtxOFhySGk5ClFxR1
+    F2RTRBODNURllqcUx6KzhnVUxZZWNzcWhSQU5DQUFTcFdpT3hxaDhNbEp5NHdrMzY
+    yeUc2Ykkwemt3U0IKcXZOVStxUldpK1lYcklUc2duOTIvZ1Z4WDVBb0swbitzNUx4
+    N2ZwanhrQVJWaTY2U0Y2elRKblgKLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=
+
+  # DATATRACKER_MEETECHO_API_BASE: "https://meetings.conf.meetecho.com/api/v1/"
+  DATATRACKER_MEETECHO_CLIENT_ID: "this-is-the-meetecho-client-id"  # secret
+  DATATRACKER_MEETECHO_CLIENT_SECRET: "this-is-the-meetecho-client-secret"  # secret
+
+  # DATATRACKER_MATOMO_SITE_ID: "7"  # must be present to enable Matomo
+  # DATATRACKER_MATOMO_DOMAIN_PATH: "analytics.ietf.org"
+
+  CELERY_PASSWORD: "this-is-a-secret"  # secret
+
+  DATATRACKER_APP_API_TOKENS_JSON: "{}"  # secret 
+
+  # use this to override default - one entry per line
+  # DATATRACKER_CSRF_TRUSTED_ORIGINS: |-
+  #   https://datatracker.staging.ietf.org
+
+  # Scout configuration
+  DATATRACKER_SCOUT_KEY: "this-is-the-scout-key"
+  DATATRACKER_SCOUT_NAME: "StagingDatatracker"
\ No newline at end of file
diff --git a/k8s/kustomization.yaml b/k8s/kustomization.yaml
index 021dfe798..3f09ce65a 100644
--- a/k8s/kustomization.yaml
+++ b/k8s/kustomization.yaml
@@ -5,6 +5,9 @@ configMapGenerator:
     files:
       - settings_local.py
 resources:
+  - beat.yaml
+  - celery.yaml
   - datatracker.yaml
+  - django-config.yaml
   - memcached.yaml
   - rabbitmq.yaml
\ No newline at end of file
diff --git a/k8s/memcached.yaml b/k8s/memcached.yaml
index 93665c9cf..f34876718 100644
--- a/k8s/memcached.yaml
+++ b/k8s/memcached.yaml
@@ -1,3 +1,42 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: memcached
+spec:
+  replicas: 1
+  revisionHistoryLimit: 2
+  serviceName: memcached
+  selector:
+    matchLabels:
+      app: memcached
+  template:
+    metadata:
+      labels:
+        app: memcached
+    spec:
+      securityContext:
+        runAsNonRoot: true
+      containers:
+        - image: "memcached:1.6-alpine"
+          imagePullPolicy: IfNotPresent
+          args: ["-m", "1024"]
+          name: memcached
+          ports:
+            - name: memcached
+              containerPort: 11211
+              protocol: TCP
+          securityContext:
+            allowPrivilegeEscalation: false
+            capabilities:
+              drop:
+                - ALL
+            readOnlyRootFilesystem: true
+            # memcached image sets up uid/gid 11211
+            runAsUser: 11211
+            runAsGroup: 11211
+      dnsPolicy: ClusterFirst
+      restartPolicy: Always
+      terminationGracePeriodSeconds: 30
 ---
 apiVersion: v1
 kind: Service