Guide
Send Email from a Kubernetes CronJob
A digest at 8am, an alert when a backup finishes, a weekly report — these are CronJobs, and they need to send email. Running an SMTP sidecar in a cluster is overkill, and embedding a mailbox password in a manifest is a security review failure. The Nylas CLI sends over API, so a CronJob calls one command with the API key pulled from a Secret. This guide gives a hardened CronJob manifest with a non-root container, a Secret-backed key, and resource limits.
Written by Aaron de Mello Senior Engineering Manager
Command references used in this guide: nylas email send, nylas auth config, and nylas email list.
How do you send email from a Kubernetes CronJob?
You send email from a CronJob by running a container that has the Nylas CLI, authenticating with an API key, and calling nylas email send on the schedule. Because the CLI sends over HTTPS, the pod needs no SMTP sidecar and no mail daemon — the job is a single short-lived container that exits after sending. A Kubernetes CronJob runs it on a cron schedule, per the Kubernetes CronJob docs.
Bake the CLI into a small image so the job starts fast rather than installing on every run — a slim base plus the install script in the Dockerfile keeps the image lean. The API key comes from a Kubernetes Secret, never the manifest, so the credential is managed separately from the workload definition and can be rotated without redeploying the CronJob.
# Build a tiny image with the CLI baked in (multi-stage, slim runtime)
# Dockerfile
FROM debian:stable-slim
RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates \
&& curl -fsSL https://cli.nylas.com/install.sh | bash \
&& apt-get purge -y curl && rm -rf /var/lib/apt/lists/*
ENV PATH="/root/.config/nylas/bin:${PATH}"
USER 10001How do you keep the API key out of the manifest?
Keep the key out of the manifest by storing it in a Secret and referencing it with secretKeyRef, which injects it as an environment variable at runtime. The CronJob spec names the Secret; the key value lives only in the Secret object, ideally managed by an external secrets controller rather than committed YAML. This is the difference between a manifest you can put in Git and one you can't.
Create the Secret once, then reference it from the job. Never inline the key in env.value or a ConfigMap — those are not secret stores. For production, source the Secret from a manager like External Secrets so the value is never in your repo at all, and grant the CronJob's service account read access to only that Secret.
# Create the Secret (or sync it from a secrets manager)
kubectl create secret generic nylas-api \
--from-literal=api-key="$NYLAS_API_KEY"What does the hardened CronJob manifest look like?
The manifest runs the CLI image on a schedule with a locked-down security context: non-root user, read-only root filesystem, all Linux capabilities dropped, and explicit resource requests and limits. These controls aren't optional hardening theater — a CronJob that sends email needs no write access to its filesystem and no elevated privileges, so denying them shrinks the attack surface to nothing useful.
The container command authenticates from the injected key and sends. Set restartPolicy: Never and a sensible backoffLimit so a failed send retries a bounded number of times instead of looping forever. The schedule below runs daily at 08:00; adjust the cron expression for your cadence.
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-email
spec:
schedule: "0 8 * * *"
jobTemplate:
spec:
backoffLimit: 2
template:
spec:
restartPolicy: Never
securityContext:
runAsNonRoot: true
runAsUser: 10001
seccompProfile: { type: RuntimeDefault }
containers:
- name: send
image: registry.example.com/nylas-cli:3.1.16
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: { drop: ["ALL"] }
resources:
requests: { cpu: "50m", memory: "64Mi" }
limits: { cpu: "200m", memory: "128Mi" }
env:
- name: NYLAS_API_KEY
valueFrom:
secretKeyRef: { name: nylas-api, key: api-key }
command: ["/bin/sh","-c"]
args:
- >
nylas auth config --api-key "$NYLAS_API_KEY" &&
nylas email send --to team@example.com
--subject "Daily report" --body "Generated at 08:00 UTC." --yesNext steps
- Cron email without Postfix — the host-cron equivalent
- Deploy agent accounts with Docker — containerizing the CLI
- Automate email reports — what to send on a schedule
- Send email from a bash script — the command the job runs
- Jenkins email notifications — pipeline post-build email
- Sync email to S3 — archive mail from a scheduled job
- Full command reference — every flag and subcommand documented