Vortos
Setup

Project Setup

Cross-platform first-run setup for Docker and local Vortos development.

Project Setup

Vortos includes an interactive setup command for first-run project configuration:

php vortos setup

The command works on Linux, macOS, and Windows. It uses Symfony Console prompts and PHP filesystem APIs instead of Bash scripts, so it does not depend on rm, sh, stty, tput, or Unix symlinks.

Composer First Run

New projects run setup automatically through Composer:

composer.json
{
  "scripts": {
    "post-create-project-cmd": [
      "@php vortos setup --first-run"
    ]
  }
}

The setup command can be skipped by Composer with Composer's normal --no-scripts flag, then run later:

composer create-project vortos/vortos my-app --no-scripts
cd my-app
php vortos setup

Profiles

Profiles are shortcuts for common setup paths:

php vortos setup --profile=docker
php vortos setup --profile=minimal
ProfileRuntimeCacheMessagingUse when
dockerFrankenPHP DockerRedisKafkaYou want the current full Docker stack
minimalLocal PHPIn-memoryIn-memoryYou want the quickest local setup
customInteractive choiceInteractive choiceInteractive choiceYou want to choose each setup category

Profiles do not mean every application must use the same infrastructure. They are shortcuts over the capabilities Vortos currently ships. Future packages can add more choices.

Interactive setup asks for a profile first, then shows a review step:

Continue
Customize
Cancel

Choose Customize to walk through each setup category before setup writes files:

CategoryCurrent choices
RuntimeFrankenPHP Docker, PHP-FPM Docker, Local PHP
Write databasePostgreSQL (DBAL), PostgreSQL (Doctrine ORM)
Read databaseNone, MongoDB
CacheRedis, in-memory cache
MessagingKafka, in-memory messaging
MCPInstall Vortos MCP server, skip Vortos MCP server

Some categories have only one supported option today. They are still part of the custom flow so future packages can add more choices without changing how setup works.

Setup choices are registered as capabilities, not hardcoded in the setup command. The command reads capabilities by category and renders whatever is available.

Automatic Package Installation

When you choose a stack option that requires a package not yet installed, setup automatically runs composer require for the missing packages before finishing.

In --dry-run mode, setup prints the composer command instead of running it — useful for verifying what would be installed without making changes.

Example: choosing "PostgreSQL (Doctrine ORM)" automatically installs vortos/vortos-persistence-orm if it is not already present in your vendor/ directory.

For Docker profiles, setup may add targeted Composer platform ignores for PHP extensions that are installed inside the generated Docker image but not necessarily installed on the host PHP CLI. For example, a Docker stack using MongoDB, Redis, and Kafka can run:

composer require --ignore-platform-req=ext-mongodb --ignore-platform-req=ext-redis --ignore-platform-req=ext-rdkafka ...

This keeps first-run setup fast: setup does not start or build Docker containers just to run Composer.

If composer install fails for any reason, setup prints the manual command to run so you can complete the step yourself:

composer require vortos/vortos-persistence-orm

Adding Setup Options From Modules

A module can add a new setup option by registering a SetupCapabilityInterface service tagged with vortos.setup_capability.

Use this key format:

category.option

Supported categories:

CategoryPurposeExample keys
runtimePHP runtime and process modelruntime.roadrunner
write_dbPrimary write databasewrite_db.mysql
read_dbOptional read/projection databaseread_db.postgres
cacheCache drivercache.memcached
messagingMessage broker or in-memory drivermessaging.rabbitmq
mcpAI client MCP integrationmcp.enabled

Example module registration:

MySqlPersistenceExtension.php
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Vortos\Setup\Capability\StaticSetupCapability;

$container->register('vortos.mysql.setup_capability', StaticSetupCapability::class)
    ->setArgument('$key', 'write_db.mysql')
    ->setArgument('$label', 'MySQL')
    ->setArgument('$category', 'write_db')
    ->setArgument('$composerPackages', ['vortos/vortos-persistence-mysql'])
    ->addTag('vortos.setup_capability')
    ->setPublic(false);

That makes the option appear in the matching Customize step. The module still needs to provide the actual implementation behind the choice: services, config defaults, environment mapping, Docker support if needed, migrations if needed, and tests.

For a complete step-by-step guide covering all integration points — including the BUILT_IN_CAPABILITIES constant, the SetupCapabilityRegistry, key naming rules, load order, and ResetInterface requirements — see Adding Optional Modules.

Presets

Use the interactive menu or pass a preset explicitly:

php vortos setup --preset=docker-frankenphp
php vortos setup --preset=docker-phpfpm
php vortos setup --preset=local
php vortos setup --preset=minimal
PresetRuntimeCacheMessagingBest for
docker-frankenphpDocker + FrankenPHPRedisKafkaDefault full-stack development
docker-phpfpmDocker + PHP-FPM/NginxRedisKafkaTeams standardizing on Nginx/PHP-FPM
localLocal PHPIn-memoryIn-memoryFast local work without Docker services
minimalLocal PHPIn-memoryIn-memoryLearning, examples, CI smoke checks

What Setup Writes

Setup writes generated local values to .env.

Do not commit .env, .vortos-setup.json, or generated backup files such as *.bak.*. Commit .env.example as the safe template for new projects.

Setup also publishes module config stubs into config/*.php when they do not already exist. These files are application-owned override points. Setup skips existing config files unless you explicitly publish configs with a force option through the config command.

config/services.php is not generated by setup. It is part of the project skeleton and is intended for application service registration.

The generated .env is grouped by section so related values stay together.

APP_ENV and APP_DEBUG are set based on the selected preset:

PresetAPP_ENVAPP_DEBUGHEALTH_DETAILS
docker-frankenphpprodfalsenever
docker-phpfpmprodfalsenever
localdevtruedebug
minimaldevtruedebug

Example output for a local preset:

.env
# App
APP_ENV=dev
APP_DEBUG=true
APP_NAME=my_app

# Write Database
VORTOS_WRITE_DB_DRIVER=postgres
VORTOS_WRITE_DB_USER=postgres
VORTOS_WRITE_DB_PASSWORD=...
VORTOS_WRITE_DB_NAME=my_app
VORTOS_WRITE_DB_DSN=pgsql://postgres:...@write_db:5432/my_app

# Read Database
VORTOS_READ_DB_DRIVER=mongo
VORTOS_READ_DB_USER=root
VORTOS_READ_DB_PASSWORD=...
VORTOS_READ_DB_DSN=mongodb://root:...@read_db:27017
VORTOS_READ_DB_NAME=my_app

# Cache
VORTOS_CACHE_DRIVER=redis
VORTOS_CACHE_DSN=redis://redis:6379
VORTOS_CACHE_PREFIX=dev_my_app_

# Messaging
VORTOS_MESSAGING_DRIVER=kafka
VORTOS_MESSAGING_DSN=kafka://kafka:9092

# Security
JWT_SECRET=...
HEALTH_DETAILS=never
HEALTH_TOKEN=...
HEALTH_EXPOSE_ERRORS=false

If a capability is not selected, its service-specific env vars are not written. For example, choosing read_db.none does not write VORTOS_READ_DB_USER or VORTOS_READ_DB_PASSWORD.

Framework modules read Vortos env names, not vendor-specific container env names:

PurposeFramework env
Write database driverVORTOS_WRITE_DB_DRIVER
Write database DSNVORTOS_WRITE_DB_DSN
Read database driverVORTOS_READ_DB_DRIVER
Read database DSNVORTOS_READ_DB_DSN
Read database nameVORTOS_READ_DB_NAME
Cache driverVORTOS_CACHE_DRIVER
Cache DSNVORTOS_CACHE_DSN
Messaging driverVORTOS_MESSAGING_DRIVER
Messaging DSNVORTOS_MESSAGING_DSN
MongoDB cursor signing secretVORTOS_CURSOR_SECRET

Docker services require vendor-specific env names (POSTGRES_PASSWORD, MONGO_INITDB_ROOT_USERNAME, etc.). Setup writes agnostic equivalents (VORTOS_WRITE_DB_PASSWORD, VORTOS_READ_DB_USER, etc.) to .env. The published docker-compose.yaml maps these to vendor-specific names via environment: blocks — vendor names never appear in your env files.

Use the files this way:

FileCommit?Purpose
.envNoGenerated local values, secrets, selected drivers, service DSNs
.env.exampleYesExample values and notes for new projects
.vortos-setup.jsonNoLocal setup state used by repeat setup runs

Setup generates passwords and stores them in .env. It does not ask for credentials during normal first-run setup. Existing secrets are preserved on rerun unless you pass --regenerate-secrets.

For Docker presets, setup publishes Docker files for the selected runtime. The generated compose file is capability-aware: unselected optional services are removed. For example, choosing read_db.none, in-memory cache, and in-memory messaging removes MongoDB, Redis, Kafka, and the worker service from the published compose file.

For local presets, setup configures in-memory cache and messaging so the framework can boot without Redis or Kafka.

MCP Setup

The Docker and local profiles install the optional Vortos MCP server by default. The minimal profile skips it by default. You can override this explicitly:

php vortos setup --profile=docker --mcp
php vortos setup --profile=docker --no-mcp

When MCP is selected, setup installs vortos/vortos-mcp. In interactive setup it then asks:

Configure MCP in an AI client now? (yes/no) [no]:

If you choose yes, setup runs:

php bin/console vortos:mcp:install

The MCP install command auto-detects supported AI clients. If multiple clients are detected, it prompts you to choose one or all detected clients. Supported clients include Codex, Claude Code, Cursor, and Windsurf.

The AI client later starts the server command automatically:

php /path/to/project/bin/console vortos:mcp:serve

Do not run vortos:mcp:serve manually during normal use; it waits for MCP protocol messages on STDIN.

Repeat Runs

php vortos setup is safe to run more than once.

It detects previous setup state in .vortos-setup.json, preserves generated secrets, reports unchanged values, and creates backups before replacing existing Docker files.

php vortos setup --profile=minimal
php vortos setup --profile=docker

Changing choices is allowed, but infrastructure-sensitive changes still require developer judgment. For example, switching runtime is safe; switching databases after migrations have been applied should be reviewed before changing production data.

Dry Run

Preview setup without writing files:

php vortos setup --profile=docker --dry-run --no-interaction

Use this in CI to verify the setup path without modifying the workspace.

Environment Checks

Setup checks:

CheckWhy it matters
PHP versionVortos requires PHP 8.2+
JSON/OpenSSL extensionsRequired by framework services and token generation
Project writabilityRequired to write .env, Docker files, config stubs, and setup state
Docker CLIRequired for Docker presets
Redis/Mongo/Kafka extensionsRequired locally when those services are selected outside Docker

The command reports checks as OK or Needs attention. It does not hide missing tools.

Non-Interactive Setup

For CI, templates, and automated scaffolding:

php vortos setup \
  --profile=docker \
  --no-interaction

Useful flags:

FlagMeaning
--profile=...Select a setup profile without prompts
--preset=...Select a setup without prompts
--dry-runPreview changes only
--regenerate-secretsGenerate new local secrets and service passwords
--skip-docker-publishConfigure env/state but do not publish Docker files
--no-docker-overwriteSkip changed Docker files
--no-docker-backupOverwrite Docker files without .bak files
--publish-migrationsInclude migration publishing in the final checklist
--run-migrationsInclude migration application in the final checklist
--mcp / --no-mcpInstall or skip the optional MCP server package

Local Development Without Docker

Vortos can run without Docker.

Use:

php vortos setup --profile=minimal
php -S 127.0.0.1:8000 -t public

The local preset uses in-memory cache and messaging by default. If you want local PostgreSQL, Redis, MongoDB, or Kafka, install the service and required PHP extension locally, then adjust .env.

Production

Do not use in-memory cache or messaging for production. They are for local development, tests, and simple examples.

On this page