---
name: oro-devops
description: OroCommerce DevOps and infrastructure reference covering system requirements, installation, deployment, Docker, cron setup, Supervisor, monitoring, caching, environment configuration, CI/CD, and performance optimization. Use when setting up local dev environments, configuring CI/CD, debugging deployment issues, or planning infrastructure. For OroCloud-specific operations (production deployment, upgrades, backups), see the oro-cloud skill.
---

# OroCommerce DevOps

Infrastructure, deployment, and operations reference for OroCommerce.
Docs: https://doc.oroinc.com/backend/setup/

**Note:** Production runs on OroCloud Enterprise. For cloud deployment,
upgrades, backups, and maintenance commands, see the `oro-cloud` skill.
This skill covers local dev, CI, and general infrastructure concepts.

## System Requirements

### Minimum Server
- 2 CPU cores, 2GB RAM, SSD
- Linux recommended (RHEL, Ubuntu, Debian, Oracle Linux)

### Software Stack

| Component | Version | Notes |
|---|---|---|
| PHP | >= 8.4 | CLI + FPM |
| PostgreSQL | >= 16.1 | with uuid-ossp extension |
| Nginx or Apache | latest | Nginx recommended |
| Node.js | >= 22 | asset build only |
| NPM | > 10 | asset build only |
| Redis | 7.2.x | cache + sessions (recommended) |
| Elasticsearch | >= 8.4.1, < 9.0 | EE only |
| RabbitMQ | 3.12.x | EE only |
| Supervisor | latest | MQ consumer daemon |
| Gotenberg | >= 8.5 | PDF generation (optional) |

### Required PHP Extensions
ctype, curl, fileinfo, gd, intl (ICU >= 4.4), json, mbstring, sodium,
openssl, pgsql, pcre, simplexml, tokenizer, xml, zip, imap, soap, bcmath,
ldap, mongodb (for GridFS)

### PHP Settings
```ini
date.timezone = UTC
detect_unicode = Off
memory_limit = 1G
; Increase for schema updates:
max_execution_time = 300
```

## Environment Configuration

Oro uses `.env-app` and `.env-app.local` for environment variables:

```bash
ORO_ENV=prod
ORO_SECRET=<random-secret>
ORO_DB_URL=postgres://user:pass@localhost:5432/oro_db?sslmode=disable&charset=utf8&serverVersion=17
ORO_MQ_DSN=amqp://user:pass@localhost:5672/%2f     # or dbal: for CE
ORO_SEARCH_URL=elastic-search://localhost:9200       # EE only
ORO_REDIS_URL=redis://localhost:6379
ORO_REDIS_CACHE_DSN=redis://localhost:6379/1
ORO_REDIS_DOCTRINE_DSN=redis://localhost:6379/2
ORO_REDIS_LAYOUT_DSN=redis://localhost:6379/3
ORO_SESSION_DSN=redis://localhost:6379/0
ORO_MAILER_DSN=smtp://localhost:1025
ORO_WEBSOCKET_SERVER_DSN=//0.0.0.0:8080
ORO_WEBSOCKET_FRONTEND_DSN=//*:8080/ws
ORO_WEBSOCKET_BACKEND_DSN=tcp://127.0.0.1:8080
```

## Installation

```bash
# Install dependencies
composer install --prefer-dist --no-dev --optimize-autoloader

# Install application (creates DB schema, loads fixtures, builds assets)
php bin/console oro:install --env=prod \
    --organization-name="My Org" \
    --user-name=admin \
    --user-email=admin@example.com \
    --user-firstname=Admin \
    --user-lastname=Admin \
    --user-password=<password> \
    --application-url=https://app.example.com \
    --timeout=3600
```

Verify requirements: `php bin/console oro:check-requirements -vv`

## Deployment Steps

```bash
# 1. Pull code
git pull origin main

# 2. Install dependencies
composer install --prefer-dist --no-dev --optimize-autoloader

# 3. Run migrations
php bin/console oro:migration:load --env=prod

# 4. Update extended entity schema
php bin/console oro:entity-extend:update-schema --env=prod

# 5. Clear and warmup cache
php bin/console cache:clear --env=prod
php bin/console oro:entity-extend:cache:warmup --env=prod

# 6. Build assets
php bin/console oro:assets:build --env=prod

# 7. Update cron definitions
php bin/console oro:cron:definitions:load --env=prod

# 8. Restart consumers
supervisorctl restart oro-consumer:*
```

## Cron Setup

Single system crontab entry runs Oro's dispatcher every minute:

```crontab
*/1 * * * * www-data /usr/bin/php /var/www/oro/bin/console oro:cron --env=prod > /dev/null
```

The `oro:cron` command dispatches scheduled commands to the MQ.

## Message Queue Consumers

Use Supervisor to keep MQ consumers running:

```ini
# /etc/supervisor/conf.d/oro-consumer.conf
[program:oro-consumer]
command=/usr/bin/php /var/www/oro/bin/console oro:message-queue:consume --env=prod --memory-limit=512
process_name=%(program_name)s_%(process_num)02d
numprocs=2
autostart=true
autorestart=true
startsecs=5
user=www-data
redirect_stderr=true
stdout_logfile=/var/log/oro/consumer_%(process_num)02d.log
```

Scale `numprocs` based on MQ load.

## WebSocket Server

For real-time notifications:

```ini
[program:oro-websocket]
command=/usr/bin/php /var/www/oro/bin/console gos:websocket:server --env=prod
autostart=true
autorestart=true
user=www-data
```

## Docker

### Two Docker Compose Patterns

**Pattern 1: Backing services only (CI / DDEV / local PHP)**

docker-compose.yml at project root provides only infrastructure services.
App runs on host or via DDEV/Symfony CLI:

```yaml
services:
  pgsql:
    image: oroinc/pgsql:17.4-alpine
    ports: ['5432']
    environment:
      POSTGRES_USER: oro_db_user
      POSTGRES_DB: oro_db
      POSTGRES_PASSWORD: oro_db_pass
    volumes:
      - postgres:/var/lib/postgresql/data
    healthcheck:
      test: "pg_isready -U$${POSTGRES_USER} -d$${POSTGRES_DB}"
      interval: 5s
      timeout: 30s
      start_period: 40s
  redis:
    image: redis:7.4.2-alpine
    ports: ["6379"]
  elasticsearch:
    image: elasticsearch:8.17.4
    command: bin/elasticsearch -Eingest.geoip.downloader.enabled=false
    environment:
      discovery.type: "single-node"
      xpack.security.enabled: "false"
      ES_JAVA_OPTS: -Xms2g -Xmx2g
  rabbitmq:
    image: oroinc/rabbitmq:3.13.7-alpine
    ports: ['5672']
  mongodb:
    image: mongo:8.0.4
    ports: ["27017"]
  mailcatcher:
    image: schickling/mailcatcher
    ports: ['1025', '1080']
```

**Pattern 2: Full stack (PHP-FPM + Nginx) for team/staging-like dev**

Separate docker-compose in `.docker/` with app container, nginx reverse proxy, Xdebug:

```yaml
services:
  oro:
    build:
      context: ./php
      dockerfile: ./Dockerfile-6.1
    environment:
      COMPOSER_AUTH: '{"github-oauth": {"github.com": "${GITHUB_TOKEN}"}}'
      XDEBUG_SESSION: PHPSTORM
      PHP_IDE_CONFIG: "serverName=localdev.example.com"
    volumes:
      - ../:/usr/share/nginx/html
  nginx:
    image: nginx:stable-alpine
    ports: ["8280:80", "2443:443"]
    depends_on: [oro]
    volumes:
      - ../public:/usr/share/nginx/html/public
      - ./nginx/default.conf.template:/etc/nginx/templates/default.conf.template:ro
  postgres:
    image: postgres:16-alpine
    ports: ['25432:5432']
    volumes:
      - ./postgres/create_uuid-ossp_ext.sql:/docker-entrypoint-initdb.d/create_uuid-ossp_ext.sql
  rabbitmq:
    build:
      context: .
      dockerfile_inline: |
        FROM rabbitmq:3.12-management-alpine
        RUN wget -O $RABBITMQ_HOME/plugins/rabbitmq_delayed_message_exchange-3.12.0.ez \
          https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases/download/v3.12.0/rabbitmq_delayed_message_exchange-3.12.0.ez && \
          rabbitmq-plugins enable --offline rabbitmq_delayed_message_exchange
    ports: ['25672:15672']
```

Notes:
- PostgreSQL requires `uuid-ossp` extension. Init script: `CREATE EXTENSION IF NOT EXISTS "uuid-ossp";`
- RabbitMQ delayed message exchange plugin is required for Oro delayed messages
- Adminer (`adminer:latest` on port 28080) is useful for DB inspection during dev

### Service Container Reference

| Service | Image | Health Check |
|---|---|---|
| PostgreSQL | oroinc/pgsql:17.4-alpine | `pg_isready` |
| Redis | redis:7.4.2-alpine | `redis-cli ping` |
| Elasticsearch | elasticsearch:8.17.4 | `curl localhost:9200/_cluster/health` |
| RabbitMQ | oroinc/rabbitmq:3.13.7-alpine | port 5672 |

## Environment Files

Oro uses `.env-app` (not `.env`) as the primary env file:

| File | Purpose |
|---|---|
| `.env-app` | Default values, committed to repo |
| `.env-app.local` | Local overrides (gitignored) |
| `.env-app.test` | Test-specific overrides |

Key variable prefix: `ORO_` (not `APP_`).

## CI/CD (Jenkins Pattern)

Jenkins pipeline with parallel build stages:

```groovy
stage('Build') {
    parallel {
        stage('Build:prod') {
            // composer install --no-dev, docker build, oro:install
        }
        stage('Build:test') {
            // composer install (with dev), phpcs, phpmd, unit tests, functional tests
        }
    }
}
```

Static analysis tools in CI:
- `bin/phpcs --standard=PSR2 src/` (PHP CodeSniffer)
- `bin/phpmd src/ xml vendor/oro/platform/build/phpmd.xml` (PHP Mess Detector)
- Unit: `phpunit --testsuite=unit src/`
- Functional: `phpunit --testsuite=functional src/`

Image push to OroCloud registry: `harborio.oro.cloud/{project}/orocommerce-enterprise-application:{tag}`

## Monitoring

### Application Health
```bash
php bin/console oro:check-requirements -vv
php bin/console debug:router --env=prod    # verify routes
php bin/console oro:message-queue:consume --help  # MQ status
```

### Key Metrics to Monitor
- MQ queue depth (DBAL: `SELECT count(*) FROM oro_message_queue`)
- Consumer process count (Supervisor)
- PHP-FPM pool status
- PostgreSQL connection count, slow queries
- Redis memory usage
- Elasticsearch cluster health
- Disk usage (cache, logs, file storage)

### Logging
Oro uses Monolog (Symfony standard). Config in `config/config_prod.yml`.
Docs: https://doc.oroinc.com/backend/logging/

## Performance Optimization

- Enable OPcache for PHP
- Use Redis for cache + sessions (not filesystem)
- Use Elasticsearch for search (EE)
- Use RabbitMQ instead of DBAL transport (EE)
- Tune PostgreSQL (shared_buffers, work_mem, effective_cache_size)
- Use `--no-debug` for all prod commands
- Optimize Composer autoloader: `composer dump-autoload --optimize`
- Docs: https://doc.oroinc.com/backend/setup/system-requirements/performance-optimization/
