Kubernetes CronJob that submits a nightly 30-second Wan 3.0 clip

A CronJob manifest using curlimages/curl and a Secret: one dated Idempotency-Key per night, concurrency forbidden, and a month of reserves at each resolution.

3 min readSume
All posts

A Kubernetes CronJob can submit a nightly 30-second Wan 3.0 clip with nothing but curl: the container posts to /v1/videos, sends an Idempotency-Key containing the date, and exits. A pod that Kubernetes restarts then cannot create a second job for the same night. Each 720p run reserves $3.75.

The manifest

Create the Secret first: kubectl create secret generic sume --from-literal=api-key=.... $$(date ...) is how Kubernetes passes a literal $(date ...) to the shell.

apiVersion: batch/v1
kind: CronJob
metadata: { name: wan-nightly-30s }
spec:
  schedule: "0 6 * * *"
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      backoffLimit: 0
      template:
        spec:
          restartPolicy: Never
          containers:
            - name: submit
              image: curlimages/curl:8.10.1
              env:
                - name: SUME_API_KEY
                  valueFrom: { secretKeyRef: { name: sume, key: api-key } }
              command: ["sh", "-c"]
              args:
                - |
                  curl -fsS -X POST https://api.sume.com/v1/videos \
                    -H "Authorization: Bearer $SUME_API_KEY" \
                    -H "Content-Type: application/json" \
                    -H "Idempotency-Key: nightly-$$(date -u +%F)" \
                    -d '{"model":"wan-3.0","prompt":"Time-lapse of a city street from dusk to night","duration":30,"resolution":"720p","aspect_ratio":"16:9","callback_url":"https://hooks.example.com/sume"}'

Why these settings

  • concurrencyPolicy: Forbid and backoffLimit: 0 keep a slow night from stacking submits. The dated key is the real guard: the same date means the same job.
  • curl -f makes HTTP errors a non-zero exit, so a 402 insufficient_credits shows up as a failed Job in kubectl get jobs.
  • callback_url must be HTTPS. The receiver should verify the signature headers described in Webhooks and refuse to run with an empty secret.

Thirty nights of reserves

One run a day for 30 days, 30 seconds each, at the three resolutions:

Sume list price, read 2026-10-08
ResolutionPer night30 nights
480p$1.875$56.25
720p$3.75$112.50
1080p$7.50$225.00

Reading the outcome

The container logs the submit response, which contains the job id and polling_url. With callback_url set, your receiver gets the completion event; without it, poll the polling_url from the submit response. A failed submit exits non-zero because of curl -f, and kubectl logs job/<name> shows the error body.

Keep the Secret in the same namespace as the CronJob, rotate it by updating the Secret rather than the manifest, and never echo $SUME_API_KEY in the args.

Keep the balance ahead of the schedule

Sume reserves the price at submit and fails the call with 402 insufficient_credits if the balance cannot cover it. A nightly job therefore needs at least one night's reserve in the balance at 06:00; set a reminder when it drops below a week's worth.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume