Rule: if it listens for HTTP requests, it is a service. If it runs and exits, it is a job.

Create:
```
gcloud run jobs create [JOB] --image [IMAGE] --region [REGION] --task-timeout 10m
```
Run it:
```
gcloud run jobs execute [JOB] --region [REGION] --wait
```

Why not a service for batch work:
- Services bill for request handling and idle min-instances; a job bills only while the task runs.
- Jobs support task retries, parallel tasks (--tasks N), and timeouts natively. Rebuilding that on a service is wasted code.
- A service that "runs once per request" still pays the HTTP server tax and cold starts per trigger.

Job specifics agents miss:
- --task-timeout caps execution (default 10 minutes, max higher). Long ETL needs this raised deliberately.
- --tasks runs N copies in parallel with CLOUD_RUN_TASK_INDEX and CLOUD_RUN_TASK_COUNT env vars for sharding. Free parallelism for batch splits.
- Retries: --max-retries on failure. Combine with idempotent task logic.
- Trigger from Cloud Scheduler with an HTTP target hitting the jobs execute API, or from a Pub/Sub push to a small service that executes the job.

Environment: jobs take the same --set-env-vars and --set-secrets as services. The container still needs no HTTP server, but keep the image lean since it starts per execution.

Verify: execute once with --wait, check exit code, then inspect with `gcloud run jobs executions list`.