Manage DLC (Deep Learning Containers) distributed training jobs — submit, inspect, stop, stream logs, and more.
job submit
pai dlc job submit --name NAME --image IMAGE --command CMD [flags]Parameter | Type | Default | Description |
--name | string | — | Job name Required |
--command | string | — | Command to run on each node Required |
--image | string | — | Container image (applied to all roles that don't specify their own image) |
--job-type | string | PyTorchJob | Framework type |
--workspace-id | string | config | Workspace ID |
Pay-as-you-go (ECS instance type) | |||
--worker-spec | string | — | Worker ECS instance type (e.g., ecs.gn6v-c8g1.2xlarge) |
--worker-count | int | 1 | Number of worker replicas |
Subscription (resource quota) | |||
--resource-id | string | — | Resource quota or resource group ID |
--cpu | string | — | Number of CPU cores (e.g., 8) |
--memory | string | — | Memory size (e.g., 64Gi) |
--gpu | string | — | Number of GPUs (e.g., 1) |
--gpu-type | string | — | GPU model (e.g., A100) |
--shared-memory | string | — | Shared memory size (e.g., 16Gi) |
Multi-role topology (mutually exclusive with --worker-*) | |||
--role | stringArray | — | Role definition in NAME:key=value,... format. Repeatable. Valid NAME values: chief / evaluator / graphlearn / head / master / ps / worker. Keys: count / cpu / gpu / gpu-type / memory / shared-memory / spec / image / restart-policy / spot / spot-strategy / spot-price-limit / spot-discount-limit / oversold-type / chief |
--role-json | stringArray | — | Role extension fields in NAME:{PascalCase JSON} format. Repeatable. Use for fields not covered by --role, such as SystemDisk and ExtraPodSpec. |
Data and code | |||
--data-source | stringArray | — | Data source configuration. Repeatable. Shorthand: id[:mount_path[:version]]; key-value: uri=oss://bucket/path/, mount-path=/mnt/data, enable-cache=true |
--code-source | string | — | Code source ID |
--code-branch | string | — | Code branch (requires --code-source) |
--code-commit | string | — | Code commit hash (requires --code-source) |
--code-mount-path | string | — | Mount path for code |
--thirdparty-lib | stringArray | — | Pre-installed Python packages (e.g., numpy==1.26.4). Repeatable. |
--thirdparty-lib-dir | string | — | Directory containing requirements.txt |
Scheduling and run control | |||
--priority | int32 | 0 | Scheduling priority, 1–9 (0 uses the server-side default) |
--scheduling-strategy | string | — | Scheduling strategy |
--success-policy | string | — | Distributed success policy |
--max-running-time-minutes | int | 0 | Maximum running time in minutes (0 = unlimited) |
--allow-nodes | string | — | Allowed scheduling nodes (comma-separated node names) |
--deny-nodes | string | — | Excluded nodes (comma-separated node names) |
--env | stringArray | — | Environment variables in KEY=VALUE format. Repeatable. |
--accessibility | string | — | Visibility |
--description | string | — | Job description |
--image-username | string | — | Username for private image registry |
--image-password | string | — | Password for private image registry |
--credential-config | string | — | Credential configuration JSON |
--elastic-spec | string | — | Elastic scaling configuration JSON |
--settings | string | — | Advanced job settings JSON |
--user-vpc | string | — | VPC network configuration JSON |
Wait and debug | |||
--wait | bool | false | Block after submit until the job reaches a terminal state |
--wait-for | string | Succeeded | Target status for --wait |
--wait-interval | int | 10 | Polling interval in seconds |
--wait-timeout | int | 3600 | Maximum wait time in seconds |
--dry-run | bool | false | Print the request body without submitting |
--generate-skeleton | bool | false | Print a request template and exit. Defaults to JSON; add |
--body-json | string | — | Full CreateJob request body (PascalCase field names). Accepts inline JSON, |
--from-file | string | — | Read the job from a YAML or JSON file. Field names correspond to CLI flags without leading dashes (name, command, worker-count…). CLI flags override file values, which is useful for saving a job template and tweaking one field per run. Use |
--batch | string | — | Batch-submit all job files (.json/.yaml/*.yml, same format as --from-file, one job per file) in a directory. All files are validated before submission. Requires |
--override / --override-string / --override-unset | stringArray | — | Modify request body fields before submission. See --override request body editing |
Common examples
# 1. Pay-as-you-go: single-role Worker training (simplest form)
pai dlc job submit \
--name ddp-demo \
--image pytorch:2.0.1-gpu-py310-cu118-ubuntu20.04 \
--command "torchrun --nproc_per_node=8 train.py" \
--worker-spec ecs.gn6v-c8g1.8xlarge \
--worker-count 4
# 2. Subscription: use resource quota (cpu/gpu/memory instead of spec)
pai dlc job submit \
--name ddp-prepaid \
--image pytorch:2.0.1-gpu-py310-cu118-ubuntu20.04 \
--command "torchrun --nproc_per_node=8 train.py" \
--resource-id quota-a1b2c3d4e5f6 \
--worker-count 4 \
--cpu 8 --memory 64Gi --gpu 1 --gpu-type A100
# 3. Multi-role topology: different specs per role (--role is mutually exclusive with --worker-*)
pai dlc job submit \
--name tf-ps-demo \
--job-type TFJob \
--command "python train.py" \
--role chief:count=1,spec=ecs.gn6v-c8g1.8xlarge \
--role worker:count=4,spec=ecs.gn6v-c8g1.8xlarge \
--role ps:count=2,spec=ecs.g7.16xlarge \
--success-policy ChiefWorker
# 4. Data source: two syntaxes (shorthand vs key-value)
# Shorthand: registered dataset ID + mount path
pai dlc job submit --name ds-demo --image pytorch:2.1 \
--command "python train.py" --worker-spec ecs.g7.xlarge \
--data-source d-abc123:/mnt/data
# Key-value: specify OSS path directly (no pre-registered dataset needed)
pai dlc job submit --name ds-demo --image pytorch:2.1 \
--command "python train.py" --worker-spec ecs.g7.xlarge \
--data-source uri=oss://my-bucket/path/,mount-path=/mnt/data
# 5. --dry-run: preview the request body without submitting
pai dlc job submit --name test --image pytorch:2.1 \
--command "python train.py" --worker-spec ecs.g7.xlarge --dry-run
# 6. --generate-skeleton + --body-json advanced workflow
# Step 1: Generate a YAML template with per-field comments
pai dlc job submit --generate-skeleton -o yaml > job.yaml
# Step 2: Edit job.yaml — remove unneeded fields, fill in values
# Step 3: Submit with --body-json (accepts both JSON and YAML)
pai dlc job submit --body-json @job.yaml
# 7. --from-file job template workflow (field names = flag names, more intuitive than --body-json)
cat > job.yaml << 'EOF'
name: bert-pretrain
image: pytorch:2.1
command: torchrun train.py
worker-spec: ecs.gn6v-c8g1.8xlarge
worker-count: 4
EOF
pai dlc job submit --from-file job.yaml
# Override file fields with CLI flags: reuse the same template with a different name
pai dlc job submit --from-file job.yaml --name bert-pretrain-lr001
# 8. --batch: batch-submit job files from a directory
pai dlc job submit --batch jobs/ --dry-run # Preview first
pai dlc job submit --batch jobs/ --yes # Actually submitThe --generate-skeleton -o yaml output includes a comment on every field that explains its purpose and the corresponding CLI flag. Use it to configure advanced fields that the CLI doesn't expose directly. For day-to-day use, prefer CLI flags or --from-file; reach for --body-json only when you need API-level field names.
job list
pai dlc job list [flags]Parameter | Type | Default | Description |
--workspace-id | string | config | Workspace ID |
--all-workspaces | bool | false | Query across all accessible workspaces |
--status | string | — | Filter by status (Running / Succeeded / Failed, etc.) |
--display-name | string | — | Filter by job name |
--job-type | string | — | Filter by framework type (PyTorchJob / TFJob, etc.) |
--show-own | bool | false | Show only jobs submitted by the current user |
--since | string | 7d | Time window (e.g., 7d, 48h, 365d). Jobs outside this window aren't returned |
--sort-by | string | — | Sort field (e.g., GmtCreateTime) |
--order | string | — | Sort direction |
--page | int | 1 | Page number |
--page-size | int | 20 | Items per page |
--columns | string | — | Custom output columns. Default: job_id, display_name, job_type, status, gmt_create_time, duration |
job get
pai dlc job get <job-id> [--brief]Parameter | Type | Default | Description |
--brief | bool | false | Compact output — omit Pods / Specs / History details |
job stop / delete
pai dlc job stop <job-id> [-y] [--dry-run]
pai dlc job delete <job-id> [-y] [--dry-run]Parameter | Type | Default | Description |
-y, --yes | bool | false | Skip the confirmation prompt |
--dry-run | bool | false | Preview the operation without executing it |
Stopped jobs can't be recovered. In-progress training data is lost.
job logs
pai dlc job logs <job-id> [--pod <pod-id>] [flags]When --pod isn't specified, the command automatically selects the job's primary pod (master, chief, driver, etc.). The selection is printed to stderr so that stdout stays clean. If the primary pod can't be determined, the command lists all pods for you to choose from instead of guessing.
Parameter | Type | Default | Description |
--pod | string | Auto-select primary pod | Pod ID. Omit to read from the job's primary pod |
--pod-uid | string | — | Pod UID, used to distinguish same-named pods after a restart |
--all-pods | bool | false | Read all pods at once, prefixing each log line with its source pod. --tail applies per-pod — use a small value for multi-node jobs. Mutually exclusive with --pod and --follow |
--follow | bool | false | Stream logs continuously (poll until interrupted) |
--since | string | 7 days ago | Show only logs after the specified time (e.g., 30m, 2h, 3d). For jobs that ended more than 7 days ago, widen this value explicitly to avoid InvalidTimeRange errors |
--tail | int | 2000 | Show only the last N lines (counted from newest) |
job events
pai dlc job events <job-id> [--pod <pod-id>] [flags]Read system events for a DLC job or one of its pods. Events record scheduling and lifecycle information that the training output doesn't cover: why a pod is still Pending, what killed it, or when it was restarted. They answer questions that job logs can't — a pod that never started has no logs, but it does have events.
Parameter | Type | Default | Description |
--pod | string | — | Read events for this pod (uses a different API). Omit to read the job's own events |
--all-pods | bool | false | Read events for all pods at once, prefixing each line with its source pod. --max applies per-pod — use a small value for multi-node jobs |
--pod-uid | string | — | Pod UID, used to distinguish same-named pods after a restart (requires --pod) |
--max | int | 2000 | Maximum number of events to return (API MaxEventsNum) |
--since | string | 7d | Show only events after the specified time (e.g., 30m, 2h, 3d). Widen this for long-running jobs |
# See what the server did with the job
pai dlc job events dlc1abc2def3ghi
# Find out why a specific pod isn't Running
pai dlc job events dlc1abc2def3ghi --pod dlc1abc2def3ghi-master-0
# Read all pods at once, 50 events each
pai dlc job events dlc1abc2def3ghi --all-pods --max 50
# Widen the time window for long-running jobs
pai dlc job events dlc1abc2def3ghi --since 30dRecommended troubleshooting order: run job events first to see what happened at the scheduling layer (Pending reasons, OOMKilled, restart records), then run job logs to see what the training process itself output.
job exec
pai dlc job exec <job-id> [--pod <pod-id>] -- <command>...Execute a command in a pod of a DLC job and print the output. A single quoted argument preserves shell syntax: pai dlc job exec <job-id> -- "nvidia-smi | head -20". Multiple tokens are re-quoted individually so that spaces are preserved, but shell syntax passed as separate tokens is forwarded literally.
Parameter | Type | Default | Description |
--pod | string | Auto-select primary pod | Target Pod ID. Omit to auto-select using the same logic as |
--pod-uid | string | — | Pod UID, used to reject stale targets after a pod restart |
--timeout | int | 120 | Execution timeout in seconds |
Two limitations from the transport channel (the pod's Web Terminal, a single pseudo-terminal): ① stdout and stderr are merged into stdout (like a real terminal); the exit code is recovered by echoing it from the shell, so the image must contain a POSIX shell. ② Non-interactive: no input is sent — commands that prompt for input hang until --timeout.
job resubmit
pai dlc job resubmit <job-id> [flags]Rebuild the submit request from an existing job and send it again. Useful for rerunning failed jobs or rerunning with tweaks. Scalar parameters (--command, --priority, --name, etc.) replace the corresponding fields. Topology and list parameters (--worker-count, --env, etc.) are rejected — use --export to export, edit, and resubmit instead.
Parameter | Type | Default | Description |
--name | string | Original name | New job name, useful for associating it with the original |
--command / --priority / --job-type and other scalar parameters | — | Original values | Replace the corresponding fields before resubmitting |
--export | bool | false | Print the rebuilt request body without submitting. Edit it, then submit with |
--dry-run | bool | false | Preview the rebuilt request body without sending |
GetJob doesn't return all original fields (e.g., SuccessPolicy, JobMaxRunningTimeMinutes, some data source options). These fields aren't automatically restored — re-specify them through flags when needed. Use --export first to check which fields were actually restored.
# Rerun as-is
pai dlc job resubmit dlc1abc2def3ghi
# Rerun with a new name and command
pai dlc job resubmit dlc1abc2def3ghi --name resnet50-v2 \
--command "torchrun --nproc_per_node=8 train.py --batch-size 512"
# Export to a file, edit, then resubmit
pai dlc job resubmit dlc1abc2def3ghi --export -o yaml > job.yaml
pai dlc job submit --body-json @job.yamljob wait
pai dlc job wait <job-id> [--for Succeeded]Parameter | Type | Default | Description |
--for | string | Succeeded | Target status |
--interval | int | 10 | Polling interval in seconds |
--timeout | int | 3600 | Maximum wait time in seconds |
The wait also ends immediately when the job reaches any other terminal state (Succeeded, Failed, or Stopped).