Virtwork configuration flows through four sources, merged in priority order:
CLI flags > environment variables (VIRTWORK_*) > YAML config file > built-in defaults
This document is the authoritative reference: every flag, every environment variable, every YAML key, every ConfigMap entry, and every per-workload parameter. For the summary, see the top-level README. For the implementation, see internal/config/config.go.
| Surface | When to use |
|---|---|
| CLI flags | One-off overrides; whatever you type wins |
Environment variables (VIRTWORK_*) |
CI/CD pipelines; ConfigMap-driven in-cluster deployments |
YAML config file (--config path.yaml) |
Reproducible runs; expressing per-workload overrides |
| Defaults | Sensible behavior with no arguments at all |
These apply to both virtwork run and virtwork cleanup.
| Flag | Env Var | YAML Key | Default | Description |
|---|---|---|---|---|
--namespace |
VIRTWORK_NAMESPACE |
namespace |
virtwork |
Kubernetes namespace for VMs and supporting resources |
--kubeconfig |
VIRTWORK_KUBECONFIG |
kubeconfig |
empty (in-cluster, then ~/.kube/config) |
Path to kubeconfig file (see kubeconfig resolution) |
--config |
— | — | empty | Path to a YAML config file to merge in |
--verbose |
VIRTWORK_VERBOSE |
verbose |
false |
Switch the logger from INFO to DEBUG |
--audit |
VIRTWORK_AUDIT |
audit |
true |
Enable SQLite audit tracking |
--no-audit |
— | — | unset | Hard override: disable audit tracking regardless of --audit / VIRTWORK_AUDIT |
--audit-db |
VIRTWORK_AUDIT_DB |
audit_db |
virtwork.db |
Path to the audit SQLite file (use /data/virtwork.db in-cluster) |
Audit precedence note: --no-audit short-circuits everything. Otherwise, the audit boolean follows the standard chain. In-cluster, set VIRTWORK_AUDIT=false in the ConfigMap to disable.
| Flag | Env Var | YAML Key | Default | Description |
|---|---|---|---|---|
--workloads |
— | (handled via workloads map per-key) |
All nine built-in workloads, sorted: chaos-disk, chaos-network, chaos-process, cpu, database, disk, memory, network, tps |
Comma-separated list of workloads to deploy. When --from-catalog is set without --workloads, the built-in default is cleared. |
--vm-count |
— | workloads.<name>.vm_count |
1 |
VMs per workload (multi-VM workloads sum their RoleDistribution() counts, so 1 becomes 2 for network and tps) |
--cpu-cores |
VIRTWORK_CPU_CORES |
cpu_cores / workloads.<name>.cpu_cores |
2 |
CPU cores per VM (per-workload override beats global) |
--memory |
VIRTWORK_MEMORY |
memory / workloads.<name>.memory |
2Gi |
Memory per VM |
--disk-size |
VIRTWORK_DATA_DISK_SIZE |
data_disk_size |
10Gi |
Data-disk size for storage-backed workloads (disk, database, chaos-disk) |
--container-disk-image |
VIRTWORK_CONTAINER_DISK_IMAGE |
container_disk_image |
quay.io/containerdisks/fedora:42 |
Boot image for the VMs; set to the golden image for faster boot |
--dry-run |
VIRTWORK_DRY_RUN |
dry_run |
false |
Print the VM YAML and exit; do not connect to a cluster |
--no-wait |
— | (sets wait_for_ready=false) |
unset | Skip readiness polling after VMs are created |
--timeout |
VIRTWORK_TIMEOUT |
timeout |
600 |
Readiness timeout in seconds |
--ssh-user |
VIRTWORK_SSH_USER |
ssh_user |
virtwork |
Username for the in-VM user account (only created when at least one SSH credential is provided) |
--ssh-password |
VIRTWORK_SSH_PASSWORD |
ssh_password |
empty | Password for the SSH user (plain-text in the VM spec — prefer keys) |
--ssh-key |
— | ssh_authorized_keys (YAML list) |
empty | Inline SSH public key; repeatable |
--ssh-key-file |
— | (handled as inline key after reading) | empty | Path to a public key file; repeatable |
--vm-concurrency |
VIRTWORK_VM_CONCURRENCY |
vm-concurrency |
10 |
Max concurrent VM creation operations |
--params |
VIRTWORK_PARAMS |
— | empty | Comma-separated per-workload params (workload.key=value); merged on top of YAML params blocks |
--catalog-dir |
VIRTWORK_CATALOG_DIR |
catalog_dir |
~/.virtwork/catalog |
Path to catalog directory containing workload entry subdirectories |
--from-catalog |
VIRTWORK_FROM_CATALOG |
from_catalog |
empty | Catalog entry names to load (comma-separated). When set without --workloads, built-in defaults are cleared — only catalog entries run. |
| (env only) | VIRTWORK_SSH_AUTHORIZED_KEYS |
ssh_authorized_keys |
empty | Comma-separated list of inline keys (env-var form) |
Note:
--ssh-key-fileis a CLI-only convenience that reads a public-key file from disk at runtime and merges its content into thessh_authorized_keyslist. There is no YAML or environment-variable equivalent — to configure SSH keys via YAML orVIRTWORK_SSH_AUTHORIZED_KEYS, supply the fully-realized public-key strings directly.
| Flag | Env Var | YAML Key | Default | Description |
|---|---|---|---|---|
--delete-namespace |
— | — | false |
Also delete the namespace itself after deleting managed resources |
--run-id |
— | — | empty | Limit cleanup to resources labeled virtwork/run-id=<uuid> |
--dry-run |
— | — | false |
Print intent without actually deleting |
--yes / -y |
— | — | false |
Skip confirmation prompt and proceed with cleanup |
Global flags (--namespace, --kubeconfig, --audit, --no-audit, --audit-db, --verbose) also apply.
| Variable | Type | Default | Source | Description |
|---|---|---|---|---|
VIRTWORK_AUDIT |
bool | true |
flag-bound | Enable audit tracking |
VIRTWORK_AUDIT_DB |
string | virtwork.db |
flag-bound | Audit SQLite path |
VIRTWORK_CONTAINER_DISK_IMAGE |
string | quay.io/containerdisks/fedora:42 |
flag-bound | VM boot image |
VIRTWORK_CPU_CORES |
int | 2 |
flag-bound | Default per-VM CPU cores |
VIRTWORK_DATA_DISK_SIZE |
string | 10Gi |
flag-bound | Default data disk size |
VIRTWORK_DRY_RUN |
bool | false |
flag-bound | Default for --dry-run |
VIRTWORK_KUBECONFIG |
string | empty | flag-bound | Kubeconfig path (takes precedence over KUBECONFIG; see kubeconfig resolution) |
VIRTWORK_MEMORY |
string | 2Gi |
flag-bound | Default per-VM memory |
VIRTWORK_NAMESPACE |
string | virtwork |
flag-bound | Target namespace |
VIRTWORK_SSH_AUTHORIZED_KEYS |
string (csv) | empty | env-only | Comma-separated SSH public keys |
VIRTWORK_SSH_PASSWORD |
string | empty | flag-bound | SSH password |
VIRTWORK_SSH_USER |
string | virtwork |
flag-bound | SSH username |
VIRTWORK_TIMEOUT |
int | 600 |
flag-bound | Readiness timeout seconds |
VIRTWORK_VERBOSE |
bool | false |
flag-bound | Verbose logging |
VIRTWORK_PARAMS |
string | empty | env-only | Comma-separated per-workload params (workload.key=value); merged on top of YAML params |
VIRTWORK_CATALOG_DIR |
string | ~/.virtwork/catalog |
flag-bound | Path to catalog directory |
VIRTWORK_FROM_CATALOG |
string (csv) | empty | flag-bound | Catalog entry names to load |
VIRTWORK_VM_CONCURRENCY |
int | 10 |
flag-bound | Max concurrent VM creation operations |
VIRTWORK_WAIT_FOR_READY |
bool | true |
flag-bound | Inverse of --no-wait |
VIRTWORK_COMMAND |
string | empty | deployment only | In-pod auto-run command: run, cleanup, or empty (sleep). Read by entrypoint.sh, not Viper. |
VIRTWORK_ARGS |
string | empty | deployment only | Extra arguments when VIRTWORK_COMMAND is set. Read by entrypoint.sh, not Viper. |
VIRTWORK_COMMAND and VIRTWORK_ARGS are container entrypoint behavior — they tell entrypoint.sh whether to run virtwork immediately or to sleep until invoked via oc exec. See deployment.md.
Full schema with every supported key:
# Global defaults
namespace: virtwork-prod
container_disk_image: quay.io/opdev/virtwork-disk:latest # set to golden image to skip first-boot package installs
data_disk_size: 20Gi
cpu_cores: 2
memory: 2Gi
# Cluster connection (omit to use in-cluster or ~/.kube/config)
kubeconfig: /etc/virtwork/kubeconfig
# Behavior
dry_run: false
verbose: false
wait_for_ready: true
timeout: 900
# SSH (optional — when omitted, no user account is created in the VM)
ssh_user: virtwork
ssh_password: "" # prefer keys
ssh_authorized_keys:
- ssh-ed25519 AAAAC3Nz... key-1
- ssh-ed25519 AAAAC3Nz... key-2
# Audit
audit: true
audit_db: /data/virtwork.db
# Catalog workloads (optional — deploy custom workloads without Go code)
catalog_dir: /path/to/catalog # default: ~/.virtwork/catalog
from_catalog:
- my-stress
- my-benchmark
# Per-workload overrides (everything optional; unspecified keys inherit globals)
workloads:
cpu:
enabled: true # explicitly enable (can be omitted; defaults to enabled)
vm_count: 2
cpu_cores: 4
memory: 4Gi
params:
cpu-load-percent: "50" # default 100
cpu-method: "matrixprod" # default all
memory:
vm_count: 1 # enabled by default when not specified
params:
memory-percent: "60" # default 80
vm-stressors: "2" # default 1
vm-method: "flip" # default all
disk:
enabled: false # skip this workload entirely
params:
block-size-rw: "8k" # default 4k
block-size-seq: "256k" # default 128k
rwmixread: "50" # default 70
numjobs: "8" # default 4
runtime: "600" # default 300
database:
cpu_cores: 2
memory: 4Gi
params:
scale-factor: "100" # default 50
clients: "20" # default 10
duration: "600" # default 300
network:
vm_count: 1 # creates 1 server + 1 client = 2 VMs
params:
parallel-streams: "8" # default 4
test-duration: "120" # default 60
tps:
vm_count: 1 # creates 1 server + 1 client = 2 VMs
params:
file-size: "50M" # default 10M
iterations: "10" # default 30
duration: "30" # default 60 (seconds per iteration)
chaos-disk:
params:
mount: /mnt/data # default /mnt/data
fill-percent: "80" # default 90
fill-sleep: "120" # default 60
release-sleep: "60" # default 30
chaos-network:
params:
latency-ms: "250" # default 100
packet-loss-percent: "10" # default 5.0
chaos-process:
params:
signal: "SIGKILL" # default SIGTERM
interval: "15" # default 30
min-pid: "500" # default 1000The enabled field controls whether a workload is deployed when listed in the --workloads flag. This provides a declarative way to disable workloads in YAML config without modifying command-line flags.
enabled: true— workload is deployed (explicit)enabled: false— workload is skipped entirely; no VMs, services, or resources created- Field omitted — workload is enabled by default (treated as
true)
Precedence: The --workloads flag determines the candidate set; the YAML enabled field then filters it. For example:
virtwork run --workloads cpu,disk,network --config config.yamlWith config.yaml:
workloads:
disk:
enabled: falseResult: Only cpu and network are deployed. disk is skipped despite being in the --workloads flag.
Audit event: When a workload is skipped due to enabled: false, an audit event of type workload_skipped is recorded with the message Workload "name" disabled via config (enabled: false).
Each workload's params block accepts string-valued keys. The current parameters per workload:
| Workload | Key | Default | Effect |
|---|---|---|---|
| tps | file-size |
10M |
Size of the HTTP test file — a positive integer followed by K, M, or G (e.g. 512K, 10M, 1G). Decimal values and binary suffixes like MiB are not supported. |
| tps | iterations |
30 |
Number of test iterations |
| tps | duration |
60 |
Seconds per iteration |
| chaos-disk | mount |
/mnt/data |
Mountpoint of the data disk to fill |
| chaos-disk | fill-percent |
90 |
Target fill percentage |
| chaos-disk | fill-sleep |
60 |
Seconds held at target fill |
| chaos-disk | release-sleep |
30 |
Seconds empty before refilling |
| chaos-network | latency-ms |
100 |
netem egress delay |
| chaos-network | packet-loss-percent |
5.0 |
netem egress drop rate |
| chaos-process | signal |
SIGTERM |
Signal sent to victims |
| chaos-process | interval |
30 |
Seconds between kills |
| chaos-process | min-pid |
1000 |
Minimum PID considered eligible |
| cpu | cpu-load-percent |
100 |
Target CPU load percentage for stress-ng (--cpu-load) |
| cpu | cpu-method |
all |
CPU stressor method for stress-ng (--cpu-method) |
| memory | memory-percent |
80 |
Target memory usage percentage (--vm-bytes) |
| memory | vm-stressors |
1 |
Number of VM worker stressors (--vm) |
| memory | vm-method |
all |
Memory stressor method for stress-ng (--vm-method) |
| disk | block-size-rw |
4k |
Block size for the random read/write fio profile |
| disk | block-size-seq |
128k |
Block size for the sequential write fio profile |
| disk | rwmixread |
70 |
Read percentage in the mixed read/write fio profile |
| disk | numjobs |
4 |
Number of parallel fio jobs for the mixed read/write profile |
| disk | runtime |
300 |
Runtime in seconds for each fio profile |
| database | scale-factor |
50 |
pgbench initialization scale factor (-s) |
| database | clients |
10 |
Number of concurrent pgbench clients (-c) |
| database | duration |
300 |
Seconds per pgbench benchmark run (-T) |
| network | parallel-streams |
4 |
Number of parallel iperf3 streams (-P) |
| network | test-duration |
60 |
Seconds per iperf3 test run (-t) |
deploy/configmap.yaml ships the following defaults. Edit and oc apply -k deploy/ to change behavior of an in-cluster pod.
| ConfigMap Key | Default | Notes |
|---|---|---|
VIRTWORK_NAMESPACE |
virtwork |
The pod creates VMs in this namespace |
VIRTWORK_CONTAINER_DISK_IMAGE |
quay.io/containerdisks/fedora:42 |
Change to the golden image to speed up boot |
VIRTWORK_DATA_DISK_SIZE |
10Gi |
|
VIRTWORK_CPU_CORES |
2 |
|
VIRTWORK_MEMORY |
2Gi |
|
VIRTWORK_WAIT_FOR_READY |
true |
|
VIRTWORK_TIMEOUT |
600 |
|
VIRTWORK_DRY_RUN |
false |
|
VIRTWORK_VERBOSE |
false |
|
VIRTWORK_AUDIT |
true |
|
VIRTWORK_AUDIT_DB |
/data/virtwork.db |
Mounted from the virtwork-audit-data PVC |
VIRTWORK_SSH_USER |
virtwork |
VIRTWORK_COMMAND and VIRTWORK_ARGS are set directly in the Deployment env: (not the ConfigMap) so that updating them triggers a pod restart. See deployment.md.
deploy/secret.yaml provides VIRTWORK_SSH_PASSWORD and VIRTWORK_SSH_AUTHORIZED_KEYS separately so that credentials are not in the ConfigMap.
flowchart TD
CLI["CLI flag explicitly set\ne.g. --namespace virtwork-test"]
ENV["Environment variable\ne.g. VIRTWORK_NAMESPACE=virtwork-prod"]
YAML["YAML config file\nnamespace: virtwork-staging"]
DEF["Built-in default\n'virtwork'"]
CLI -->|wins| MERGE[Effective Config]
ENV -->|2nd| MERGE
YAML -->|3rd| MERGE
DEF -->|4th| MERGE
The kubeconfig key has an additional resolution layer handled by cluster.ResolveKubeconfigPath before the connection is established:
--kubeconfig / VIRTWORK_KUBECONFIG > KUBECONFIG env var > in-cluster service-account > ~/.kube/config
- Explicit path —
--kubeconfigflag orVIRTWORK_KUBECONFIGenv var (resolved via the standard Viper priority chain above). When set, in-cluster detection is skipped entirely. KUBECONFIGenv var — the standard Kubernetes variable. Checked only when no explicit path is provided.- In-cluster service-account —
rest.InClusterConfig()is attempted only when no kubeconfig path is resolved from steps 1–2. - Default loading rules —
~/.kube/config(viaclientcmddefault loading rules) is the final fallback.
This means setting VIRTWORK_KUBECONFIG in a CI/CD pipeline or ConfigMap will always win over KUBECONFIG, and any explicit path will bypass in-cluster detection — even when running inside a pod.
How it works in code (internal/config/config.go → LoadConfig):
SetDefaults(v)seeds Viper with the built-in defaults.v.SetEnvPrefix("VIRTWORK")+v.AutomaticEnv()enables automatic env-var binding (with-↔_replacement so--data-disk-size↔VIRTWORK_DATA_DISK_SIZE).- If
--config <path>is set,v.ReadInConfig()merges the YAML file (overrides env and defaults). bindFlagIfSet(...)walks each known flag and copies the value into Viper only if the flag was explicitly set on the command line. This makes CLI flags the top of the stack without clobbering env/YAML values from defaults.- The
Configstruct is populated from Viper's effective view.
Workload-specific config (workloads.<name>.*) is parsed as a separate map in step 5 via v.UnmarshalKey("workloads", ...). Per-workload params can also be set via the --params flag or VIRTWORK_PARAMS env var using workload.key=value syntax — these are merged on top of YAML params after step 5, so --params wins over YAML params blocks.
The --no-audit flag is checked separately by initAuditor in cmd/virtwork/main.go and short-circuits the standard chain.
- All config field definitions live in
internal/config/config.go—WorkloadConfigstruct,Configstruct,SetDefaults,BindFlags,LoadConfig. - The mapstructure tag on each field is the YAML key name (e.g.,
mapstructure:"data-disk-size"→ YAML keydata_disk_sizeafter the_/-replacement). Match this convention when adding new fields. - Per-workload
paramsare surfaced into the workload asWorkloadConfig.Params map[string]string. Each workload declares a typedParamSchema(a slice ofParamDefentries with key, type, default, and description). The orchestrator callsregistry.ValidateParams()before constructing workloads, rejecting unknown keys (with "did you mean?" suggestions) and type-mismatched values at deploy time. - When adding a new env var, prefer letting Viper bind it automatically rather than hand-rolling
os.Getenv(seeVIRTWORK_SSH_AUTHORIZED_KEYSinresolveSSHKeysandVIRTWORK_PARAMSinresolveRawParamsfor the current exceptions, driven by their non-standard value semantics). - Update this document, the ConfigMap default list, and the per-workload
paramstable whenever you add a new knob.
- README.md — configuration summary in the main project README
- chaos-workloads.md — narrative description of how chaos params shape workload behavior
- deployment.md — ConfigMap + Secret + Deployment env semantics
- development.md — adding new workloads (including new params)
- audit-schema.md — audit-related configuration consequences