Kubernetes
YAML
Scripting
DevOps
Cloud Deployment

How to include script and run it into kubernetes yaml?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Running shell scripts from Kubernetes manifests is common for setup tasks, migrations, health probes, and scheduled automation. The key is deciding where script logic should live and how it should be injected into pods safely. This article covers production-friendly patterns using ConfigMap, initContainers, and lifecycle hooks.

Decide Where the Script Should Run

Before writing YAML, choose the execution context. Different contexts solve different problems.

  • initContainer: one-time setup before the main container starts.
  • Main container command: primary process logic, often entrypoint driven.
  • Lifecycle hook: short pre-stop or post-start operations.
  • Job or CronJob: standalone task execution.

If your script initializes data or waits for dependencies, initContainer is usually best because failure is explicit and startup blocks until success.

Store Script Content in a ConfigMap

A clean pattern is storing the script body in a ConfigMap and mounting it as a file. This avoids rebuilding images for small script changes.

yaml
1apiVersion: v1
2kind: ConfigMap
3metadata:
4  name: app-scripts
5data:
6  init.sh: |
7    #!/usr/bin/env sh
8    set -eu
9    echo "Waiting for database"
10    until nc -z db 5432; do
11      sleep 2
12    done
13    echo "Database is reachable"

Then mount and run the script from an initContainer.

yaml
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4  name: web-app
5spec:
6  replicas: 2
7  selector:
8    matchLabels:
9      app: web-app
10  template:
11    metadata:
12      labels:
13        app: web-app
14    spec:
15      volumes:
16        - name: script-volume
17          configMap:
18            name: app-scripts
19            defaultMode: 0555
20      initContainers:
21        - name: wait-db
22          image: busybox:1.36
23          command: ["/bin/sh", "/scripts/init.sh"]
24          volumeMounts:
25            - name: script-volume
26              mountPath: /scripts
27      containers:
28        - name: app
29          image: ghcr.io/example/web-app:1.0.0
30          ports:
31            - containerPort: 8080

This separation keeps script logic visible and testable.

Inline Script with Command and Args

For short logic, inline commands are acceptable. Keep them minimal so debugging stays easy.

yaml
1apiVersion: batch/v1
2kind: Job
3metadata:
4  name: cleanup-job
5spec:
6  template:
7    spec:
8      restartPolicy: Never
9      containers:
10        - name: cleanup
11          image: alpine:3.20
12          command: ["/bin/sh", "-c"]
13          args:
14            - |
15              set -eu
16              echo "Removing temp files"
17              rm -rf /tmp/cache/*
18              echo "Done"

Use this style for one-off operational tasks, not large multi-step deployment logic.

Run Scripts During Container Lifecycle

Lifecycle hooks help with startup or shutdown coordination. Keep hook commands short and resilient.

yaml
1apiVersion: v1
2kind: Pod
3metadata:
4  name: api-pod
5spec:
6  containers:
7    - name: api
8      image: ghcr.io/example/api:2.3.1
9      lifecycle:
10        preStop:
11          exec:
12            command: ["/bin/sh", "-c", "echo draining && sleep 5"]
13      readinessProbe:
14        httpGet:
15          path: /health
16          port: 8080

A preStop hook is useful when your service needs a short drain period before termination.

Security and Reliability Considerations

Scripts in clusters should follow the same standards as app code.

  • Pin image versions like alpine:3.20 instead of floating tags.
  • Run as non-root where possible using pod security context.
  • Use set -eu in shell scripts so failures are explicit.
  • Externalize secrets into Kubernetes Secret, not plain ConfigMap.
  • Log meaningful status messages because script output is often your first debug signal.

You can also test scripts locally in the same container image before shipping them.

bash
docker run --rm -it -v "$PWD/scripts:/scripts" alpine:3.20 /bin/sh /scripts/init.sh

Common Pitfalls

  • Embedding very large scripts directly in args. This becomes unreadable. Move script content to a mounted file.
  • Forgetting execute permissions on mounted script files. Set defaultMode or call /bin/sh script.sh explicitly.
  • Using latest image tags. Reproducibility suffers and debugging rollbacks gets harder.
  • Mixing setup logic into the main application start command. Failures become ambiguous. Prefer initContainer boundaries.
  • Hardcoding secrets in script text. Pull credentials from environment variables backed by Kubernetes Secret resources.

Summary

  • Pick the right execution context: initContainer, main command, lifecycle hook, or Job.
  • Store non-trivial scripts in ConfigMap and mount them as files.
  • Keep inline script snippets short and operationally focused.
  • Apply secure defaults: pinned images, clear failure behavior, and secret hygiene.
  • Favor small, composable scripts that are easy to test and debug.

Course illustration
Course illustration

All Rights Reserved.