Code Engine Applications vs Batch Jobs: Picking the Right Serverless Primitive for Event-Driven Work
Choosing between Code Engine Applications and Batch Jobs for event-driven work? Batch Jobs win for intermittent, run-to-completion tasks with event triggers — here's the practical decision framework and a worked image-processing example.
09 Aug 2025, 11:23 UTC

The problem: you have intermittent work, not a persistent service
You're building an event-driven pipeline on IBM Cloud Code Engine. Maybe it's image resizing triggered by Cloud Object Storage uploads, a nightly report generator, or a webhook handler that processes bursts of incoming data. The question isn't "how do I run containers?" — it's "which Code Engine primitive actually fits this workload without wasting money or adding operational friction?"
Code Engine offers two compute models: Applications (always-on HTTP services that scale from zero) and Batch Jobs (run-to-completion containers triggered on demand). They share the same project, registry, and VPC networking, but their scaling behavior, billing model, and integration points differ in ways that directly affect architecture decisions.
Scaling and billing: the core difference
An Application deploys a container that listens for HTTP requests. When traffic arrives, Code Engine spins up instances (up to your configured max) and routes requests through a built-in load balancer with automatic TLS and custom domain support. After a configurable idle period (default 10 minutes), it scales to zero. You pay for vCPU-seconds and memory-seconds while instances are running — including the time they sit idle waiting for the next request.
A Batch Job runs a container to completion and exits. You submit a job run (optionally with 1–999 parallel instances via array indices), and Code Engine provisions resources only for the duration of execution. There's no idle scaling, no load balancer, no HTTP ingress. Billing stops the moment the container exits. For workloads that run less than roughly 15–20% of the time, Batch Jobs are typically cheaper than an Application that cold-starts frequently or stays warm between sporadic requests.
Cold-start latency is similar for both (1–5 seconds for a typical container image pull and start), but the pattern differs: Applications cold-start when the first request hits a scaled-to-zero deployment; Batch Jobs cold-start on every submission. If your traffic has sub-minute gaps, an Application may never scale to zero, effectively running 24/7 at minimum scale.
Event-driven patterns: where Batch Jobs shine
Batch Jobs integrate directly with IBM Cloud Event Notifications. You can configure a Cloud Object Storage bucket write event, a Kafka topic message, or a custom HTTP webhook to trigger a job run automatically. No persistent consumer, no polling, no message broker management. The job receives the event payload via environment variables (for COS events, the bucket and object key), processes it, and exits.
A single Code Engine project can host both an Application (your API frontend) and Batch Jobs (background workers). They share secrets, service bindings, and the same private VPC subnet. A common pattern: the Application validates an upload request, writes a reference to a queue or COS, and returns immediately; an Event Notifications trigger fires a Batch Job to do the heavy lifting — transcoding, OCR, data transformation — without blocking the HTTP response.
Worked example: serverless image processing pipeline
Suppose you need to generate three thumbnail sizes whenever a user uploads an image to a COS bucket. Here's a practical Batch Job setup.
1. Container image
Build a minimal image with your processing logic (e.g., Python + Pillow). Push to IBM Cloud Container Registry (ICR):
# Run from your local machine or CI with IBM Cloud CLI authenticated
ibmcloud cr login
ibmcloud cr region-set us-south
ibmcloud cr namespace-add my-namespace # if not exists
docker build -t us.icr.io/my-namespace/thumbnailer:v1 .
docker push us.icr.io/my-namespace/thumbnailer:v1
Run where: any shell with ibmcloud CLI and Docker. Permissions: IAM Writer role on the Container Registry namespace. Risk: public registry exposure if namespace visibility is misconfigured — verify with ibmcloud cr namespace-get my-namespace.
2. Create the Code Engine project and job
# Create project (one-time)
ibmcloud ce project create --name img-processing --region us-south
# Create Batch Job definition
ibmcloud ce job create --name thumbnailer --image us.icr.io/my-namespace/thumbnailer:v1 --cpu 2 --memory 4G --array-spec 1-3 --env JOB_INDEX="${JOB_INDEX}" --env COS_BUCKET="my-uploads" --env COS_REGION="us-south" --service-account my-ce-sa
--array-spec 1-3 launches three parallel instances per run (one per thumbnail size). Each instance reads JOB_INDEX (1, 2, or 3) to determine which size to generate. The service account my-ce-sa needs IAM Reader/Writer on the COS bucket.
3. Wire Event Notifications
In the IBM Cloud console: Event Notifications → Create destination → Code Engine Job → select thumbnailer. Then create a subscription for your COS bucket's ObjectCreated events pointing to that destination. Alternatively, via CLI:
ibmcloud en destination create --name cos-to-thumbnailer --type code_engine_job --target-job thumbnailer --target-project img-processing
ibmcloud en subscription create --name thumbnail-on-upload --destination cos-to-thumbnailer --source-type cloud_object_storage --source-bucket my-uploads --event-types ObjectCreated
Verification: Upload a test image to the bucket. In the Code Engine console, open the job → Runs tab. You should see a run with three succeeded instances. Check the bucket for generated thumbnails.
Trade-offs and hard limits
- No HTTP ingress: Batch Jobs cannot receive webhooks directly. If you need an HTTP trigger, put an Application or API Gateway in front that enqueues work (e.g., writes to a COS bucket or Kafka topic) for the Batch Job.
- 72-hour max runtime: Hard limit per job run. Long simulations or model training need checkpointing or migration to IKS Kubernetes Jobs or bare metal.
- Per-instance customization: All parallel instances share the same command and environment. Use
JOB_INDEXto partition input (e.g., fetch different object keys from a manifest file). - Regional scope: Projects are single-region. Cross-region DR requires external state (COS, databases) and re-submission logic in another region.
- Quotas: vCPU, memory, and concurrent job submissions are capped per project/region. Increases need a support ticket (24–48 hours).
Decision checklist
Use this quick filter when architecting a new workload:
- Does it need to serve HTTP traffic directly? → Application (or API Gateway + Application).
- Is it triggered by events (COS, Kafka, schedule) and runs to completion? → Batch Job.
- Does it run > 72 hours continuously? → Not a Batch Job; consider IKS or VSI.
- Is utilization < 20% with bursty, unpredictable gaps? → Batch Job avoids idle costs.
- Do you need parallel fan-out (same logic, different inputs)? → Batch Job with array indices.
Start by deploying a throwaway Batch Job in a test project (ibmcloud ce job create --name test-job --image icr.io/ibm/ibmcloud-cli:latest --command "sleep" --args "60") and submit a run. Observe the run timeline, logs, and billing metrics in the console. That 5-minute experiment tells you more than any pricing calculator.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.