Skip to main content

Jobs

Jobs are finite workloads in Quave ONE. Use them for customer-visible, one-off or repeatable commands such as Rails migrations, database maintenance scripts, data imports, and backfills.

A Job is a first-class app type, separate from Apps, Databases & Services, and Functions. Each execution creates a JobRun with its own status, command snapshot, timing, logs, and exit-code metadata.

How Jobs Work

  • Finite execution: Each run creates a Kubernetes Job instead of a long-running Deployment.
  • No public host by default: Jobs do not need a port, hosts, HTTP probes, or a health check.
  • Visible history: Every run is stored as a JobRun, so customers can see what command ran, when it started, whether it succeeded, and which logs it produced.
  • Normal resource usage while running: Job pods consume account resources while they run. Finished Kubernetes Jobs are cleaned up by their TTL.
  • Safe config changes: Editing a saved Job command creates pending changes. The new command is not used by default runs until you apply those changes.

Common Use Cases

  • Rails or Ruby migrations, for example bin/rails db:migrate or bundle exec rails db:migrate
  • One-off scripts, for example bundle exec rake users:backfill
  • Data imports or exports
  • Maintenance tasks that should not restart an app pod repeatedly when they fail
  • Repeatable customer operations that need logs and audit history

Create a Job

Via the Web UI

  1. Open the account in Quave ONE.
  2. Go to Apps.
  3. In the Jobs section, click Add new Job.
  4. Choose the deployment source, usually Use image for a pre-built image or CLI deployment for source builds.
  5. Set the Job app name, image or build configuration, region, and environment.
  6. Open the app settings and configure Job Defaults.
  7. Open the environment settings and use Job Settings if that environment needs a command or timeout override.
  8. Deploy the image or build the content before creating the first run.

Via the Public API

Create the app with dockerPreset set to JOB. A Job app does not require port.

curl -X POST \
-H 'Authorization: YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"name": "Rails migrations",
"accountId": "ACCOUNT_ID",
"dockerPreset": "JOB",
"useImage": true,
"image": "ghcr.io/acme/my-rails-app:2026-06-19",
"jobConfig": {
"command": "bin/rails db:migrate",
"shell": true,
"timeoutSeconds": 1800,
"ttlSecondsAfterFinished": 21600,
"backoffLimit": 0
},
"isCliDeployment": true
}' \
https://api.quave.cloud/api/public/v1/app

Then create an environment for that app:

curl -X POST \
-H 'Authorization: YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"accountId": "ACCOUNT_ID",
"appId": "APP_ID",
"name": "staging",
"region": "us-5",
"zClouds": 1
}' \
https://api.quave.cloud/api/public/v1/app-env

If you deploy a pre-built image, deploy it to the environment before running the Job:

curl -X POST \
-H 'Authorization: YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"appEnvId": "APP_ENV_ID",
"image": "ghcr.io/acme/my-rails-app:2026-06-19"
}' \
https://api.quave.cloud/api/public/v1/app-env/deploy-image

Via MCP

Ask your AI agent to create and configure the Job:

"Create a Job app called Rails migrations using image ghcr.io/acme/my-rails-app:2026-06-19 and command bin/rails db:migrate"

Useful MCP tools include:

ToolPurpose
create-appCreate the Job app with dockerPreset: "JOB".
create-app-envCreate the environment for the Job app.
deploy-app-env-imageDeploy a pre-built image to the Job environment.
update-app-env-job-configEdit environment-level Job settings.
apply-app-env-changesApply pending Job settings to the environment.
run-app-env-jobCreate a new JobRun.
list-app-env-job-runsList previous runs.
get-app-env-job-runCheck one run.
get-app-env-job-run-logsFetch logs for one run.
cancel-app-env-job-runCancel an active run.
rerun-app-env-job-runRerun from a previous run snapshot.

Job Settings

Saved Job settings are stored in jobConfig.

FieldDescription
commandCommand to execute. Required before a run starts unless each run provides an override.
argsOptional argument list. If shell is true, arguments can also be included directly in command.
shellRuns the command through /bin/sh -lc when true. Default: true.
workingDirOptional working directory inside the container.
timeoutSecondsActive deadline for a run. Default: 1800 seconds.
ttlSecondsAfterFinishedKubernetes TTL after completion. Default: 21600 seconds. Minimum: 300 seconds.
backoffLimitKubernetes Job retry backoff limit. Default: 0.
maxConcurrencyMaximum active runs for this Job environment.
allowConcurrentRunsAllows unlimited active runs when true and maxConcurrency is omitted. Default: false.
scheduleReserved for scheduled Jobs.
scheduleTimeZoneReserved for scheduled Jobs.

Per-run overrides only accept the safe execution fields: command, args, shell, workingDir, timeoutSeconds, ttlSecondsAfterFinished, and backoffLimit. Configure concurrency and schedule fields on the app or environment.

Config Hierarchy and Apply Changes

There are four layers of Job configuration:

  1. App defaults: Set on the Job app as jobConfig.
  2. Environment overrides: Set on one app environment as jobConfig.
  3. Applied deployment snapshot: Captured from the app and environment settings when the environment image/build is deployed or when pending Job settings are applied.
  4. Per-run override: Passed when creating one JobRun; it only affects that run.

By default, new JobRuns use the applied deployment snapshot. If a Job environment does not have an applied snapshot yet, Quave ONE falls back to the current app and environment settings. This means:

  • Editing Job Defaults on the app creates pending deploy changes for the environments of that app.
  • Editing Job Settings on the environment creates pending deploy changes for that environment.
  • A default run keeps using the previous applied command until you click Apply changes, call POST /api/public/v1/app-env/apply-changes, call the MCP tool apply-app-env-changes, or save with applyImmediately: true.
  • Passing a per-run jobConfig to run-app-env-job or POST /api/public/v1/app-env/job-runs overrides the applied snapshot for that run only.
  • Historical JobRun records are immutable. Changing the app or environment config does not rewrite older run snapshots.

Environment tokens can save pending Job config changes when scoped to that environment, but applying changes immediately requires a user token.

Run a Job

Create a run with the saved applied command:

curl -X POST \
-H 'Authorization: YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"appEnvId": "APP_ENV_ID"
}' \
https://api.quave.cloud/api/public/v1/app-env/job-runs

Or override the command for one run:

curl -X POST \
-H 'Authorization: YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"appEnvId": "APP_ENV_ID",
"jobConfig": {
"command": "bin/rails db:migrate",
"timeoutSeconds": 1800,
"backoffLimit": 0
}
}' \
https://api.quave.cloud/api/public/v1/app-env/job-runs

The response includes jobRunId, status, the command snapshot, timing values, and the content version used for the run.

Check Status and Logs

List recent runs:

curl -X GET \
-H 'Authorization: YOUR_TOKEN' \
'https://api.quave.cloud/api/public/v1/app-env/job-runs?appEnvId=APP_ENV_ID&limit=20'

Get one run:

curl -X GET \
-H 'Authorization: YOUR_TOKEN' \
'https://api.quave.cloud/api/public/v1/app-env/job-run?jobRunId=JOB_RUN_ID'

Fetch logs:

curl -X GET \
-H 'Authorization: YOUR_TOKEN' \
'https://api.quave.cloud/api/public/v1/app-env/job-run/logs?jobRunId=JOB_RUN_ID&stream=INFO'

Job statuses are QUEUED, STARTING, RUNNING, SUCCEEDED, FAILED, CANCELED, TIMED_OUT, and NOT_FOUND. Treat only SUCCEEDED as a successful terminal status. FAILED, CANCELED, TIMED_OUT, and NOT_FOUND should block a release pipeline.

Rails Migration Pattern

For a Rails migration:

  1. Build or publish the same image version that your app should migrate.
  2. Create a Job app with dockerPreset: "JOB" and that image.
  3. Set the app default command to the Rails command that exists inside your container, usually bin/rails db:migrate for apps with Rails binstubs or bundle exec rails db:migrate.
  4. Create one environment per target, for example staging and production.
  5. Deploy the image to the target Job environment.
  6. Apply pending Job setting changes if you edited the command after the deploy.
  7. Run the Job.
  8. Poll the JobRun until it is terminal.
  9. Read the logs and exit status.

Recommended config:

{
"command": "bin/rails db:migrate",
"shell": true,
"timeoutSeconds": 1800,
"ttlSecondsAfterFinished": 21600,
"backoffLimit": 0
}

Use backoffLimit: 0 when a migration should not retry automatically after a failure. If you want to run it again, use rerun-app-env-job-run or create another JobRun after checking the logs.

Gate an App Deploy on a Rails Migration

A Rails release usually needs the migration to finish before the web app rollout continues. In Quave ONE, use the same image tag for the migration Job and the app deploy, then poll the JobRun status before deploying the app.

For a complete GitHub Actions workflow, compare this sequence with the public demo repository quaveone/rails-migration-job-demo.

The API flow is:

  1. Deploy or register the new image on the migration Job environment.
  2. Create the JobRun and save the returned jobRunId.
  3. Poll GET /api/public/v1/app-env/job-run?jobRunId=JOB_RUN_ID until isTerminal is true.
  4. Continue only when status is SUCCEEDED or isSuccess is true.
  5. If the status is FAILED, CANCELED, TIMED_OUT, or NOT_FOUND, fetch logs and stop the app deploy.
  6. Deploy the web app image after the migration succeeds.

The CLI supports this as a pipeline-friendly sequence:

IMAGE="ghcr.io/acme/rails-app:${GITHUB_SHA}"

quaveone job run \
--env acme-rails-migration-prod \
--image "$IMAGE" \
--command "bin/rails db:migrate" \
--wait \
--timeout-seconds 1800 \
--backoff-limit 0 \
--logs-on-failure

quaveone deploy \
--env acme-rails-web-prod \
--image "$IMAGE" \
--wait

In CI, keep these as separate ordered steps or join them with shell &&. Because quaveone job run --wait exits non-zero for every terminal status except SUCCEEDED, the app deploy step will not run when the migration fails.

Quave ONE can gate the deploy, but Rails migrations should still be written with normal zero-downtime practices: prefer backward-compatible schema changes, avoid assuming all old pods have stopped, and inspect the JobRun logs before retrying a failed migration.

Rerun and Cancel

  • Rerun uses the previous run's immutable config snapshot and content by default. You can override contentId or provide a new per-run jobConfig.
  • Cancel deletes the Kubernetes Job for an active run and marks the JobRun as CANCELED.

Troubleshooting

  • The new command did not run: Check for pending changes. Apply them, or pass a per-run jobConfig override.
  • Job command is required: Set jobConfig.command on the app or environment, apply it, or pass jobConfig.command when creating the run.
  • Job image is not ready: Deploy an image or complete a build before running the Job.
  • Concurrency limit reached: Wait for the active run to finish, set maxConcurrency, or enable allowConcurrentRuns.
  • No logs yet: A run may still be QUEUED or STARTING. Fetch the run status first, then request logs after it reaches RUNNING or a terminal status.

For the broader product taxonomy, see App Types. For endpoint-level details, see the Job Runs API.