← Back to README · Documentation index
Configuration
Configuration (.orobox.yaml)
Orobox uses a configuration file called .orobox.yaml in the root of your bundle or project. If the file does not exist, the init command will guide you through its interactive creation.
This page is the reference for every key. For a task-oriented walkthrough of adding packages, PHP extensions, PHP settings, ports and services — with worked examples and troubleshooting — see Customizing the environment.
Example .orobox.yaml file:
type: bundle
class: MyBundle
namespace: MyVendor\Bundle\MyBundle
oro_version: "6.1"
domains:
- host: oro.demo
root: public
ssl: false
services:
redis: true
redisinsight: true
mailpit: true
rabbitmq: true
elasticsearch: false
kibana: false
adminer: true
test:
use_tmpfs: true
tmpfs_size: 1g
qa:
eslint: true
stylelint: false
commands:
- name: "otr"
command: "php bin/console oro:test:run"
description: "Runs the Shippy Pro tests suite"
service: "application"
image:
dockerfile: docker/image/Dockerfile
apk: [imagemagick]
php_extensions: [redis]
php_ini:
memory_limit: 4G
xdebug.log_level: 0
ports:
http: 8090
db: 5434
composer:
# Tokens for private repositories. Mirrors Composer's COMPOSER_AUTH schema and is
# injected only into the containers that run composer (never committed or baked
# into long-running services).
auth:
github-oauth:
github.com: ghp_yourtokenhere
http-basic:
repo.packagist.com:
username: token
password: yourtokenhere
# Forward the host SSH agent into the containers. Omit to auto-detect (any SSH-transport
# URL in the repositories below, or in the project's own composer.json); true forwards
# unconditionally, false never forwards.
ssh_agent: true
repositories:
- type: vcs
url: https://github.com/my-org/private-repo.git
- type: composer
url: https://repo.packagist.com/my-org/
# SSH-format URLs are supported too; orobox forwards your host SSH agent into
# the containers automatically (start ssh-agent and `ssh-add` your key first).
- type: vcs
url: git@github.com:my-org/ssh-repo.git
Installation types (type)
Orobox supports three installation types, selected via the type field (or the init prompt / --type flag):
bundle(default): your repository is a single bundle that gets grafted onto a prebuilt OroCommerce application. Orobox downloads the Oro app into a named volume, mounts your bundle underbundles/<namespace>, wires it viacomposer config repositories.bundle+composer require, and syncs the resolved vendor back to the host. Requiresclass/namespace.-
project: your repository is the whole OroCommerce application. Orobox bind-mounts the checkout directly onto/var/www/oro, runscomposer install(resolving dependencies from your repo’s owncomposer.lock), then runs the Oro installer. No bundlenamespace/classis needed, and no vendor is synced back to the host. Because the checkout is bound whole,var/and its subdirectories (var/logs,var/data, …) live on the host and are shared with every container — unlikebundle, wherevar/datasits in a Docker-managed volume.var/cacheis a Docker volume in every type, because the Symfony cache is far slower on a bind mount; it is not visible from the host.The project repository must contain a
composer.json(and ideally acomposer.lock). If the lock file is absent, Composer resolves fromcomposer.json.Private dependencies: declare the repositories in the application’s own
composer.jsonas usual. Orobox reads that file to decide whether to forward your SSH agent, andcomposer.ssh_agent: truein.orobox.yamlforces forwarding when the SSH access is not visible there. Tokens still go incomposer.auth. demo: identical toprojectin how sources and vendors are wired, but the stack runs production-tuned —ORO_ENV=prod, OPcache enabled withopcache.validate_timestamps=0(dev types also run OPcache, but keep timestamp checks, every 2 s),ORO_DEBUG=0, and no Xdebug compiled into the image. Intended for demo and staging instances, not for development: PHP will not pick up source edits until the container restarts.
Example project config:
type: project
oro_version: "6.1"
domains:
- host: oro.demo
root: public
ssl: false
Example demo config:
type: demo
oro_version: "6.1"
domains:
- host: demo.local
root: public
ssl: false
Environment files per install type
bundle: Orobox bind-mounts its generated.envand.env.testover the application’s.env-app.localand.env-app.test. Orobox owns those files; edit them through.orobox.yamlor by placing a.env/.env.testnext to.orobox.yaml(see Overriding the generated env files).-
projectanddemo: the checkout owns.env-app,.env-app.localand.env-app.test. On the firstorobox init, Orobox copies its generated.envto.env-app.localand.env.testto.env-app.testonly if those files do not already exist, then never touches them again — subsequentorobox up/run/testinvocations leave your edits alone.Upgrading an existing
projectsetup: earlier Orobox versions bind-mounted the internal env files over.env-app.localand.env-app.test, so your checkout may not contain them. Either re-runorobox initto have them seeded, or copy them yourself from the Orobox internal directory. If they are missing, the application starts with no database DSN.The
dbanddb-testcontainers still readPOSTGRES_*from Orobox’s internal.envand.env.test. If you changeORO_DB_*in your own.env-app.local, mirror it there, or the application and the database will disagree on credentials.
Overriding the generated env files
A .env (or .env.test) in the directory that holds the config file (.orobox.yaml, or the file given with --config) is merged key by key over the file Orobox generates; it no longer replaces it. List only the keys you change or add:
ORO_MAILER_DSN=smtp://mail:1025
MY_API_KEY=xyz
The merged file is read two ways: Symfony Dotenv reads it in order, resolving ${...} against what it has read so far, while compose resolves the env_file against the project environment, which is this same file read whole. In both, the last assignment of a key wins. The merged file is laid out so that both readings give the values you meant:
- the generated file, unchanged;
- after a
# From .envcomment (# From .env.testfor the test file), every assignment of your file, in your file’s order. It reads as your file would on its own, on top of the generated values:ORO_APP_DOMAIN=${MY_HOST}works whenMY_HOSTis a key only your file defines, andORO_INSTALL_OPTIONS="${ORO_INSTALL_OPTIONS} --timeout=0"extends the generated value once. A value that references its own key gets the generated value written in place of the reference, because a reference to itself would mean something else to compose; - after that, under a
# Values that use the keys above, read againcomment, every generated key that references a key you set and that you do not set yourself, so it follows your value: changeORO_APP_DOMAINandORO_APP_URL=http://${ORO_APP_DOMAIN}/follows. Your own keys that reference one of those are repeated too, and so on until nothing more depends on a key you changed.
In your file:
- Values are copied verbatim,
${...}references and multi-line quoted values (a PEM key, say) included, except a reference to the key’s own name, which is replaced by the generated value as described above. - Comments and blank lines are dropped; only
KEY=valueassignments matter (an optional leadingexport, leading whitespace and Windows line endings are tolerated, and if a key appears twice the last one wins).
A project that already keeps a complete copy of .env keeps working: every key it defines still overrides the generated one. The difference is that keys it does not define, for example ones added by a later Orobox release, now come from the generated file instead of being missing.
For project and demo installs the merged file is what the first orobox init copies to .env-app.local / .env-app.test, as described above. After that, the application and php-fpm-app containers read your checkout’s own .env-app.local: a later change to .env reaches only the services that load Orobox’s .env directly (ws, consumer, cron, and the databases’ credentials). Edit .env-app.local for the application itself.
Image customization (image)
Orobox runs a published image, so what it ships is the same for everyone. A project that depends on something more — a system library, pdftotext, a PECL extension, a CLI tool it shells out to — adds it under image:. Most needs fit four declarative keys and no Dockerfile at all:
image:
apk: [imagemagick, poppler-utils, ghostscript] # apk add --no-cache
php_extensions: [redis, imagick, xsl] # install-php-extensions
npm: ["@playwright/test"] # npm install -g
run: # extra RUN lines, in order
- curl -sSL https://example.com/tool -o /usr/local/bin/tool && chmod +x /usr/local/bin/tool
Orobox renders these keys into a layer on top of the published image, one RUN per key in a fixed order (apk, php_extensions, npm, run; each run entry is its own RUN, and php_extensions is preceded by a RUN that fetches install-php-extensions only when the base image lacks it), so a change to one key reuses the cached layers before it. Empty keys are skipped. Orobox does not check that a package exists — the build reports that with its own log.
Validation:
apk,php_extensionsandnpmtake plain names: letters, digits and@ . _ + : / = ~ -, starting with a letter, a digit or@(so an entry cannot be an option such as--allow-untrusted). Anything else is refused with a hint to move it torun.- A
runentry is one line. A YAML folded scalar (- >) is fine — its trailing newline is dropped — but a line break inside the entry, a trailing\or a heredoc (<<EOF) is refused, because the following text would become Dockerfile instructions of its own. Join commands with&&, or move a longer script intoimage.dockerfile. install-php-extensionscomes with the published image. If your local copy of the base image predates it, the layer downloads the same pinned release before installing the extensions, sophp_extensionsworks either way;orobox up --rebuild(ordocker pullof the base) refreshes the base itself.
The layer is built locally, tagged orobox-custom/<project>:<oro_version>-<type>, and exists only on your machine. See Custom Dockerfile below for what it costs and when it rebuilds; the same rules apply whichever keys you use.
Custom Dockerfile (image.dockerfile)
For anything the declarative keys cannot express — multi-stage builds, COPYing files in — point image.dockerfile at a Dockerfile of your own (orobox extend image creates one with the required header and sets the key for you, see extend):
image:
dockerfile: docker/image/Dockerfile
Deprecated: the top-level
dockerfilekey is the old spelling ofimage.dockerfile. It still works and is read asimage.dockerfile, but every command prints a warning asking you to move it. Setting both is an error.orobox deploy-initrewrites the file with the new key.
The path is relative to the directory holding .orobox.yaml. The file belongs to your repository: commit it, review it, change it like any other source file.
Its final stage must build on the image Orobox publishes, which arrives in the OROBOX_BASE_IMAGE build argument:
ARG OROBOX_BASE_IMAGE
FROM ${OROBOX_BASE_IMAGE}
RUN apk add --no-cache imagemagick poppler-utils
RUN install-php-extensions redis
The published image already ships install-php-extensions (at a pinned release), so adding a PHP extension is a single RUN line as above — no need to download the tool yourself.
ARG before FROM is required by Docker itself — that is the only way a FROM can read a build argument. Orobox refuses a Dockerfile whose last FROM names anything else, because a hardcoded tag would make oro_version decide nothing and break the stack somewhere far from this config key. Bumping oro_version in .orobox.yaml therefore moves your layer with it, and the Dockerfile never needs editing for an Oro upgrade.
Earlier stages are unconstrained, so compiling something and copying the result over works as usual:
ARG OROBOX_BASE_IMAGE
FROM golang:1.24-alpine AS tool
RUN go install example.com/some/tool@latest
FROM ${OROBOX_BASE_IMAGE}
COPY --from=tool /go/bin/tool /usr/local/bin/tool
How it works, and what it costs:
- Combined with the declarative keys: when
image.dockerfileand any otherimage.*key are both set, the generated lines are appended to the project’s Dockerfile, i.e. to its final stage — one build, one layer chain, no second image. The generated lines start withUSER root, because your Dockerfile may end on a non-rootUSERand installs need root. The layer therefore ends as root; where compose setsuser:for a service, that is the user it runs as. .dockerignore: a project Dockerfile built as it is (no otherimage.*key) is passed todocker build -f <path>, so a Dockerfile-specific<name>.dockerignoreworks: Docker uses it instead of<context>/.dockerignorewhen it exists. With otherimage.*keys the rendered Dockerfile is passed on stdin, and only<context>/.dockerignoreapplies.- The build context is the directory containing the Dockerfile, not the repository root (
orobox extend imageputs it indocker/image/, a directory of its own). With only the declarative keys there is nothing toCOPY, so the context is an empty directory under Orobox’s internal directory. Put the files youCOPYnext to it (docker/php.ini,docker/entrypoint-extra.sh) — a context scoped this way keeps builds fast and rebuild detection exact. - Rebuilds are automatic. There is no separate command and no need to re-run
init: whenever Orobox creates containers from the image —orobox up, andinit,test-init,db restoreortestwhen they have to start a service that is not running — it notices that the rendered Dockerfile (so any change to animage.*key), a file in its build context, or the base image changed, and rebuilds first. Commands that work inside the containers already running (testandqaon a running stack,shell,console,run,xdebug) neither rebuild nor switch to a new layer: onlyorobox upmoves running containers to it.orobox self-updatetherefore only pulls, and the layer follows on the nextorobox up. - When nothing changed, the check walks the build context (the path, size and modification time of every file in it) and runs one or two
docker image inspect— no build runs. Because every file there counts, keep runtime data out of the build context: recipe directories (docker/varnish/,docker/sftp/upload/), fixtures and uploads change while the stack runs, and each change would rebuild the image and recreate the PHP containers.orobox extend addwarns when a recipe’s files would land inside it. orobox up --rebuildpulls the base image first, then builds with--no-cache, for what Docker’s cache cannot see: an unpinnedRUN apk addthat should pick up a newer package.- The image is tagged
orobox-custom/<project>:<oro_version>-<type>and exists only on your machine. It is per checkout, so two projects on the same host never share one. - Removing every
image.*key puts the project straight back on the published image, with no local build at all.
Build the image, not the code. Your sources are bind-mounted into the container at run time, so
COPYing them into the image gains nothing and makes every edit a rebuild. Use this for tools and libraries.
PHP settings (php_ini)
To change a PHP setting — a memory limit, a timezone, Xdebug options — for every PHP service, set php_ini to a map of directives. No image build is involved:
php_ini:
memory_limit: 4G
max_execution_time: 0
xdebug.log_level: 0
Orobox renders the map into zz-project.ini in its internal directory and bind-mounts it, read-only, at /usr/local/etc/php/conf.d/zz-project.ini in every PHP service (application, php-fpm-app, ws, consumer, cron) and in the install service of the setup run, so orobox init installs Oro with the same settings the stack runs with. The zz- prefix makes PHP read it after the image’s own oro-custom.ini, so your values win.
A change needs the containers recreated, not rebuilt. The bind-mount path never changes, which Docker Compose does not treat as a reason to recreate anything, so Orobox also stamps every PHP service with a dev.orobox.php-ini-hash label holding a short hash of the mounted ini. When the ini changes, the label changes, and the next orobox up recreates the containers. In the file form the hash covers the content of your own file, so editing it is picked up the same way.
Rendering rules:
- Directive names are written verbatim, dots and case included (
xdebug.log_level, not a nestedxdebug:map). They may contain only letters, digits,.,_and-. - Booleans become
On/Off; numbers are written as they are. - Constant expressions are written unquoted, so PHP evaluates them:
error_reporting: "E_ALL & ~E_DEPRECATED"becomeserror_reporting = E_ALL & ~E_DEPRECATED. A value counts as an expression when it parses as php.ini’s expression syntax, uses an operator (|,&,^,~,!), and its operands are numbers or upper-case constants (E_ALL,E_NOTICE). A lower-case word joined by an operator (dev&test) is a literal and is quoted. PHP evaluates an upper-case name it does not know as0, so a misspelled constant (E_AL & ~E_DEPRECATED) silently gives the wrong value rather than an error. - Strings that are php.ini keywords (
none,null,yes,no,on,off,true,false, in any case) are double-quoted, so they reach PHP as the word:session.cookie_samesite: NonestaysNoneinstead of becoming empty. Write a real YAML boolean (true) for an On/Off setting. - Strings containing
$are single-quoted and taken literally; php.ini would otherwise expand${VAR}from the environment. A value containing both$and'cannot be written and is refused. - Other strings are double-quoted (with
\and"escaped) when they contain whitespace (a multi-word value),;,=,#,",',{,},[,], operators or parentheses that do not form a valid expression, or when they are empty; anything else is written as it is. A value cannot span several lines. - php.ini has no nesting, so a nested map or a list value is rejected: write
xdebug.modeas a dotted key.
If you already keep an ini file in the repository, give its path instead of a map. It is mounted as-is in place of the generated one:
php_ini: docker/php.ini
The path follows the same rules as image.dockerfile: relative to the directory holding .orobox.yaml, inside the project, and free of :, ", \, $ and control characters (they would break the volume definition). It must also exist and be a file: a missing file is a configuration error reported when the config loads (and by orobox init), rather than a mount Docker would fill with an empty directory. orobox down and orobox clear are the exception — they only stop the environment, so they print a warning and go on.
php_ini stays outside image: because it is not part of the image.
Host ports (ports)
Every port the stack publishes on your machine can be moved with a ports map, for the day 8080 or 5432 is already taken by something else:
ports:
http: 8090
https: 8453
db: 5434
redis: 6380
Keys you leave out keep their default. A value of 0 means “do not publish this port on the host” (except for http and https, see below): the service stays reachable by the other containers on the compose network, it just has no host port. A key that is not in the table, or a number outside 0-65535, is a configuration error, so a typo does not silently keep the default.
| Key | Service | Default | Container port |
|---|---|---|---|
http |
web | 8080 | 80 |
https |
web (only with an SSL domain) | 8443 | 443 |
db |
db | 5432 | 5432 |
db_test |
db-test (orobox test-init) |
5433 | 5432 |
redis |
redis | 6379 | 6379 |
redisinsight |
redisinsight | 8001 | 5540 |
mail_ui |
mail (Mailpit web UI) | 8025 | 8025 |
mail_smtp |
mail (Mailpit SMTP) | 2025 | 1025 |
rabbitmq |
rabbitmq (AMQP) | 5672 | 5672 |
rabbitmq_ui |
rabbitmq (management UI) | 15672 | 15672 |
elasticsearch |
elasticsearch | 9200 | 9200 |
kibana |
kibana | 5601 | 5601 |
adminer |
adminer | 8081 | 8080 |
gotenberg |
gotenberg | 3000 | 3000 |
Only the host side moves. Containers keep talking to each other on the container ports, so ORO_DB_PORT, the Redis and RabbitMQ DSNs and the like need no change. orobox up prints the URLs and the database port of the services that are enabled using these values, and leaves out a service whose port is 0.
http and https take precedence over the deprecated nginx_http_port / nginx_https_port keys of .orobox.yaml and the ORO_NGINX_HTTP_PORT / ORO_NGINX_HTTPS_PORT environment variables, which keep working when ports does not set them (the keys must be between 1 and 65535). Between those two, the environment variable wins when both are set, as an ORO_* variable does for any key. A port left empty (db: ~) is a configuration error: write a number, or 0 to not publish it. http and https are the exception to the 0 rule: they must be between 1 and 65535, because the web port is the application’s entry point (the application URLs and the websocket frontend are built from it) and cannot be left unpublished.
Changing a port needs the containers recreated: run orobox up again.
Extending the stack (.orobox.compose.yaml)
To add a service of your own (an object store, a mock API), or to tweak one of Orobox’s, write a standard Docker Compose file next to .orobox.yaml. There is no configuration key: the files are found by name.
| File | Committed | Purpose |
|---|---|---|
.orobox.compose.yaml |
yes | The team’s additions to the stack |
.orobox.compose.local.yaml |
no (git-ignored) | One developer’s tweaks: ports, personal mounts |
Both are optional and are appended in that order after Orobox’s own compose files, so the local file wins over the team file, and both win over the generated stack. Docker Compose’s usual merge rules apply, so there is nothing new to learn:
# .orobox.compose.yaml
services:
minio:
image: minio/minio
command: server /data --console-address :9001
ports: ["9001:9001"]
volumes: [minio_data:/data]
consumer:
environment:
ORO_MQ_PREFETCH: "5"
php-fpm-app:
volumes:
- ./docker/fixtures:/var/www/oro/fixtures:ro
volumes:
minio_data:
orobox extend compose writes the team file with commented examples, and orobox extend compose --local writes the personal one and adds it to your .gitignore when the project has one (see extend). orobox create also adds .orobox.compose.local.yaml to the .gitignore of a standalone bundle it generates.
Relative paths. Write paths relative to the file, as in any compose project. Orobox runs compose from its own internal directory (~/.config/orobox/<project>/ on Linux, the platform’s user config directory elsewhere, .orobox/ in CI or with OROBOX_LOCAL_CONFIG set), so it rewrites the relative host paths of a copy of your file (./docker/fixtures becomes an absolute path under the directory that holds .orobox.yaml) and passes that copy to compose as compose.project.resolved.yaml / compose.local.resolved.yaml. Your file is never modified. The rewritten keys are:
| Key | Forms |
|---|---|
services.*.volumes |
short syntax SRC:DST[:MODE] when SRC starts with . or ~; long syntax type: bind with its source |
services.*.build |
the string form, or build.context (build.dockerfile is relative to the context and left alone; remote contexts such as https://…, git@… or github.com/… are left alone). A build: mapping without context gets the project directory: compose’s default . would be Orobox’s internal directory |
services.*.build.additional_contexts |
the mapping form and the name=path list form; docker-image://, service:, oci-layout://, target: and URL values are left alone |
services.*.build.ssh |
the path of id=path entries (a bare default names the agent and has no path) |
services.*.develop.watch |
each entry’s path |
services.*.env_file |
a string, a list of strings, or a list of {path: …} |
services.*.extends.file |
string |
services.*.label_file |
string or list |
configs.*.file, secrets.*.file |
string |
include |
a string entry, or the path / env_file / project_directory of the long form |
Values reached through YAML anchors and << merge keys are rewritten too, so a x-mounts: &mounts [...] fragment shared by several services works as written. A leading ~ expands to your home directory, as compose does (~bob/x included). Absolute paths, values starting with $ (interpolated by compose itself) and short-syntax sources without a path — named volumes — are left as they are. A $ in the project directory’s own path is escaped ($$) so compose does not interpolate it, and when that path contains a : short-syntax volumes are written in long syntax. A file may hold several YAML documents (separated by ---); a file that is empty or holds only comments counts as absent.
Services the stack does not define. Compose rejects the whole project over a service that has neither an image nor a build context. An entry that only tweaks a service the generated stack does not define for the command at hand — db-test, which exists only for orobox test and test-init, or an optional service the project disabled — is therefore left out of the copy compose reads for that command, and kept where the stack defines it. A service that the stack never defines (a disabled service, a typo) produces a warning; to add a new service, give it an image or a build. The personal file counts the team file’s services as part of the stack, so it can tweak a service the team file (or a recipe) adds. An entry that reaches services: only through a YAML merge key (<<: *extra) cannot be left out of the copy, so if it would have to be, the command stops and names it.
Build contexts across files. A build: mapping without context gets the project directory (compose’s default . would be Orobox’s internal directory) — unless an earlier document of the same file, or the team file for the personal one, already gives that service a build: compose merges build mappings key by key, so a later build: {args: …} keeps the earlier context.
Merge caveats.
-
Compose appends to sequences such as
ports. To replace the list a core service already has, tag it!override; to drop it, tag it!reset(both need Docker Compose 2.24.4 or later):services: web: ports: !override - "9090:80" orobox up --cleanandorobox clearrundown -v, which deletes the named volumes your override declares as well. Data that must survive belongs in a volume declaredexternal: true(create it once withdocker volume create).- A service with a
build:section is built byorobox upon every run (Orobox passes--build), so an edited Dockerfile takes effect. - Redefining
imageon one of Orobox’s own services (application,web,php-fpm-app,ws,consumer,cron,volume-init,web-init) detaches it fromoro_versionand from the custom image layer; Orobox prints a warning.
Project service URLs. Give a service the label dev.orobox.url and orobox up lists it under “Project services” after the built-in URLs. The value is printed as written (compose interpolation is not applied to it), and a service with profiles: is not listed, since orobox up does not start it:
services:
minio:
labels:
dev.orobox.url: http://localhost:9001
Every service of the override can also be named in orobox logs, orobox shell and in the service of a custom command.
Errors and warnings. A file that is not valid YAML, or whose top level is not a mapping, stops every command that runs compose, naming the file; Orobox never runs the stack with the override silently dropped. A bind-mount source that does not exist on the host produces a warning naming the path and what Docker will do — in short syntax it creates an empty directory there, in long syntax it refuses to start the container — because either way the problem would show up far from its cause. Sources compose still has to interpolate (${VAR}) and long-syntax binds with bind.create_host_path: true are not checked. Each warning is printed once per command.
The deploy pipeline does not use these files. orobox deploy and the generated CI build and test the application image, not your development stack; services added here exist only in local orobox commands.
Configuration Fields
Every command except init, create, extend, self-update, help, completion and version checks the configuration first and stops on an error, naming the file and the key: an unknown key, a value of the wrong type, a YAML syntax error, a path outside the project.
type: Installation type —bundle(default),projectordemo. See Installation types.class: Name of the bundle class (bundle type only).namespace: PHP namespace of the bundle (bundle type only).oro_version: OroCommerce version (e.g., “7.0”, “6.1”, “6.0”, “5.1”).domains: List of domains for the environment.services: Configuration for optional services and tools:redis: (bool) Enable/disable Redis.redisinsight: (bool) Enable/disable RedisInsight.mailpit: (bool) Enable/disable Mailpit.rabbitmq: (bool) Enable/disable RabbitMQ.elasticsearch: (bool) Enable/disable Elasticsearch/OpenSearch.kibana: (bool) Enable/disable Kibana (only if Elasticsearch is enabled).adminer: (bool) Enable/disable Adminer (PostgreSQL manager).
test:use_tmpfs: (bool) If enabled, uses RAM (tmpfs) for database files in the test container, significantly improving performance but data is lost on container restart.tmpfs_size: (string) Size of the tmpfs mount (e.g., “1g”, “512m”).qa: (map) Enable or disable individual QA tools. Any tool not listed defaults to enabled. Useful when a bundle has no JavaScript (disableeslint,stylelint) or no Twig templates (disabletwig_cs_fixer).phpstan: (bool) Enable/disable PHPStan.rector: (bool) Enable/disable Rector.php_cs_fixer: (bool) Enable/disable PHP-CS-Fixer.twig_cs_fixer: (bool) Enable/disable Twig-CS-Fixer.eslint: (bool) Enable/disable ESLint.stylelint: (bool) Enable/disable Stylelint (SCSS/LESS/SASS/CSS).
commands: (list) List of custom commands that can be run in the container:name: (string) Name of the command (e.g.,otr).command: (string) The actual command to execute (e.g.,php bin/console oro:test:run).description: (string) Description of the command (displayed in help).service: (string, optional) Default service to run the command in (e.g.,application).
image: (map, optional) Customizes the image the stack runs. See Image customization.dockerfile: (string) Path, relative to.orobox.yaml, of a project-owned Dockerfile that extends the published image. See Custom Dockerfile.apk: (list of strings) Alpine packages, installed withapk add --no-cache.php_extensions: (list of strings) PHP extensions, installed withinstall-php-extensions.npm: (list of strings) Global npm packages, installed withnpm install -g.run: (list of strings) Extra shell commands, each its ownRUN, executed last and in order.
dockerfile: (string, deprecated) Old spelling ofimage.dockerfile. Every command prints a warning on stderr while it is set (not forcompletion,helpandversion, whose output may be piped into a file).nginx_http_port,nginx_https_port: (int, deprecated) Old spelling ofports.http/ports.https, which win when both are set.php_ini: (map or string, optional) PHP settings applied to every PHP service without rebuilding the image. A map of php.ini directives (memory_limit: 4G,xdebug.log_level: 0) rendered into a mountedzz-project.ini, or the path of an ini file of your own. See PHP settings.ports: (map of ints, optional) Host port published for each service,0to not publish it. See Host ports.composer: (map) Composer-specific settings.repositories: (list,bundletype only) Additional Composer repositories to register in the OroCommerce project during installation. Accepts the same format as Composer’srepositoriesfield (VCS, Composer, path, package, etc.). These are merged with any existing repositories in the project’scomposer.json. Required when the bundle depends on packages hosted in private repositories.projectanddemoinstalls declare their repositories in the application’s owncomposer.jsonand ignore this key.auth: (map) Credentials for private repositories, using Composer’sCOMPOSER_AUTHschema (github-oauth,gitlab-token,http-basic,bearer, …). Serialized to JSON and injected as theCOMPOSER_AUTHenvironment variable only into the containers that run composer, so tokens are never committed or baked into long-running services.ssh_agent: (bool, optional) Forces SSH agent forwarding on or off. When omitted, Orobox auto-detects it: forwarding is on when an SSH-transport URL appears inrepositoriesabove or in the checkout’s owncomposer.json. Set it totruefor aprojectwhose private dependencies reach SSH through something Composer never sees (a git submodule, apathrepository that is itself a checkout), and tofalseto opt out entirely.- SSH repositories: when forwarding is on — see
ssh_agentabove — Orobox bind-mounts your host SSH agent socket into the containers and setsSSH_AUTH_SOCK(on Docker Desktop the built-in agent socket is used, on Linux your live$SSH_AUTH_SOCK). This covers both the one-shot composer/git commandsorobox initruns and the runningapplicationcontainer, soorobox shellfollowed bycomposer updateorgit fetchauthenticates too.orobox initwill start an agent and load your default key if none is running; every other command uses only an agent that is already running, so start one first witheval "$(ssh-agent)" && ssh-add.
deploy: (map,projecttype only) Deployment configuration, generated bydeploy-init. This is the single source of truth:deploy.phpreads host, user, port, path, repository and ref from theOROBOX_DEPLOY_*variables Orobox injects, so the two files cannot drift.pre_built_assets_enabled: (bool)truewhen the repository already ships built assets — the pipeline then has no assets stage.falsemakes the pipeline runoro:assets:install --env=prodand upload the result asassets.tar.gz. Either way the remote never rebuilds them.repository: (string, optional) URL Deployer clones on the remote host. Defaults togit remote get-url origin.source_dir: (string, optional) Repository-relative directory holding the OroCommerce application. Leave empty when the repository root is the application. Set it for a monorepo that keeps the application beside other projects (e.g.b2bin a repository withb2b/andapi_client/): the pipeline builds from that directory and Deployer’ssub_directoryextracts only it into the release, so the release looks like a plain Oro checkout and the sibling projects never reach the remote host. Must be relative and inside the repository.stages: (list) One entry per deploy target.name: (string) Stage selector, e.g.orobox deploy production.ref: (string) Git ref built by the pipeline and checked out on the remote, so the artifacts always match the deployed code.host,user,port: (string/string/int) SSH target.portdefaults to22.deploy_path: (string) Deployer’sdeploy_pathon the remote host.keep_releases: (int, optional) Releases kept on the remote. Defaults to5.test_suites: (list, optional) PHPUnit suites the pipeline runs:unit,functionalor both. Defaults to[unit];functionaladds a fulloro:install --env=testand is much slower.restart_command: (string, optional) Command run on the remote after the update, for consumers and cron.
Note: Versions of PHP, PostgreSQL, Node.js, and other components are automatically determined by the oro_version setting and cannot be changed manually.
Global Flags
These options can be used with any command:
--config: Specifies an alternative configuration file (default:.orobox.yaml).--debug/-d: Shows all Docker output.--agent: Minimal output for automated callers.
Agent mode
--agent reduces a command’s output to what an automated caller — an LLM agent, a script, a CI
job that parses results — actually needs. Everything Orobox says about itself is dropped:
banners, spinners, progress lines, per-tool headers and success confirmations.
What is left:
- stdout carries the payload only: QA findings, test failures, and the verbatim output of the
commands whose child process output is the result (
logs,shell,console,run,db). - stderr carries errors, one line each, prefixed
error:, with no colour. A multi-line message gets the prefix on every line, so a caller splitting on newlines cannot mistake a continuation for payload. - The exit code is unchanged from a normal run. It is what an agent reads.
A command that succeeds with nothing to report prints nothing at all:
orobox qa --agent
On a clean tree that writes zero bytes and exits 0.
Details worth knowing:
--debugwins. Passing both prints everything, as--debugalone would.--debugexists to show the full picture, and silently discarding it would be the worse surprise.- Flag only. There is no environment variable and no auto-detection from a non-terminal stdout. Agent mode is never entered because of an inherited shell or a pipe — existing CI pipelines and scripts keep the output they have today.
- Prompts are never shown. Interactive questions take their default instead of reading stdin,
so a command cannot hang waiting for an answer nobody is there to give. When a required value
has no default, the command writes one
error:line naming what to set and exits non-zero. --reportstill works.orobox qa --report=gitlab --agentwrites the report file as usual; only the summary on the terminal is suppressed.