Sunday, 06 September 2026
Advertisement Advertise Your advert could be here Reach thousands of learners and ICT professionals across Rwanda. Contact us
Advertisement Opportunity Jobs, scholarships & hackathons Fresh openings from Rwandan job boards are pulled in every hour. See openings

Configuration, secrets and stateless design

Expert DevOps: scale, reliability and security · lesson 3 of 12

In this lesson: Design an application that can be killed and replaced at any moment.

Orchestration only works if your application cooperates. A system that can move a workload between machines assumes the workload does not mind being moved. Most of the discipline below is old, unglamorous, and the reason some migrations are boring and others take a year.

Configuration comes from the environment

apiVersion: v1
kind: ConfigMap
metadata:
  name: yanjye-config
data:
  APP_ENV: production
  APP_DEBUG: "false"
  DB_HOST: yanjye-db
---
apiVersion: v1
kind: Secret
metadata:
  name: yanjye-secrets
type: Opaque
stringData:
  DB_PASSWORD: a-long-random-string
      envFrom:
        - configMapRef: { name: yanjye-config }
        - secretRef:    { name: yanjye-secrets }
A Kubernetes Secret is base64, not encryption. Anyone who can read Secrets in that namespace can read the value, and by default it sits in etcd unencrypted. Turn on encryption at rest, restrict access with RBAC, and for anything serious use an external secret store that syncs in. The name promises more than the object delivers.

Never commit secrets to Git — including your manifests

Manifests belong in version control. Secrets do not. The usual answers are sealed secrets, which encrypt a value so only the cluster can decrypt it and the encrypted form is safe to commit, or an operator that pulls values from a real secret manager at run time.

Stateless, so that any pod can serve any request

If a request can only be served by the pod that handled the previous one, you cannot scale, cannot roll out, and cannot lose a node gracefully.

StateWrong placeRight place
SessionsFiles on the podRedis, or a signed cookie
UploadsThe pod's filesystemObject storage
CacheIn-process onlyShared cache, with in-process as an optimisation
Scheduled jobsCron inside every podA CronJob, or a leader-elected worker
LogsFiles on the podStandard output, collected centrally
Cron inside a replicated pod runs N times. Scale to four replicas and the nightly billing job runs four times. This is a genuinely expensive bug and it is invisible until you scale up.

Logs go to standard output

Do not write log files. Write to stdout and stderr and let the platform collect them. A file inside a pod disappears with the pod — usually the pod that crashed, taking the evidence with it.

Shutting down properly

When Kubernetes stops a pod it sends SIGTERM, waits, then sends SIGKILL. An application that ignores SIGTERM has every in-flight request killed mid-response on every single deploy.

    spec:
      terminationGracePeriodSeconds: 30
      containers:
        - name: app
          lifecycle:
            preStop:
              exec:
                command: ["sleep", "5"]

The preStop sleep looks odd but is the standard fix for a real race: removal from the load balancer and the SIGTERM happen concurrently, so without a short pause a few requests are routed to a pod that has already begun shutting down.

Handle SIGTERM in your code

pcntl_signal(SIGTERM, function () {
    // stop accepting new work, finish what is in progress, then exit
    $this->shouldStop = true;
});

This matters most for queue workers: without it, a deploy kills jobs halfway through. With it, the worker finishes its current job and exits cleanly.

Start fast, and do not do work at boot

A pod that takes ninety seconds to become ready makes every rollout slow and every recovery slow. Warm caches lazily rather than at startup, and never run migrations from the application's own start-up path — with four replicas that is four processes migrating the same database at once. Run migrations as a separate Job before the rollout.

The test for all of this: can you kill any pod, at any moment, and have nobody notice? If the answer is no, find the reason. That reason is what will break during a node failure at 3am — and it will break in exactly the same way, only without you choosing the moment.

Try it yourself

While your application is serving traffic, run kubectl delete pod on one of its pods and watch with a load generator running. Count the failed requests. If it is not zero, you have found either a missing readiness probe, unhandled SIGTERM, or state stored in the pod.

Create a free account to save progress

All lessons in this track

  1. 1
  2. 2
  3. 3
  4. 4
    Scaling, requests and limits ~30 min account needed
  5. 5
  6. 6
  7. 7
    Running an incident ~30 min account needed
  8. 8
    Postmortems that change something ~25 min account needed
  9. 9
  10. 10
  11. 11
    What it costs, and why ~25 min account needed
  12. 12
Advertisement Yanjye Learn a new digital skill this week ICT, programming and professional courses with graded weekly assignments. Start free