Customize Infrastructure Configuration
The template configuration files, which were installed by the previous step, need to be customized for your project.
Please go through at least the files in the .platform/ folder and adapt them to your needs.
Configure your environments
The default Symfony environment for Pimcore projects is dev, you may customize that for your PaaS environments accordingly.
- In the Pimcore PaaS Console, go to
Settings->Environmentsand select an environment, e.g.production. - Open the tab
Variables - Create a new variable with the name
env:APP_ENVand the valueprod - Pimcore now runs in production mode
Install Profile
Pimcore PaaS ships with a built-in install profile (Pimcore\Paas\Install\PaasProfile) that automates the initial Pimcore installation on first deploy. The profile uses the new profile-based installer introduced in Pimcore 2026.x
What the profile configures
The PaaS profile declares the following env var definitions, which the installer collects and validates:
| Definition | Env Var | Purpose |
|---|---|---|
DatabaseEnvVarDefinition | DATABASE_URL | MySQL/MariaDB connection DSN |
OpenSearchEnvVarDefinition | PIMCORE_OPENSEARCH_DSN | OpenSearch connection DSN |
AmqpMessengerEnvVarDefinition | PIMCORE_MESSENGER_TRANSPORT_DSN_PREFIX | RabbitMQ AMQP transport DSN prefix |
MercureEnvVarDefinition | MERCURE_JWT_KEY, MERCURE_URL, MERCURE_SERVER_URL | Mercure real-time hub (on PaaS, JWT keys are derived from PIMCORE_ENCRYPTION_SECRET by the template) |
ProductRegistrationEnvVarDefinition | PIMCORE_ENCRYPTION_SECRET, PIMCORE_INSTANCE_IDENTIFIER, PIMCORE_PRODUCT_KEY | Product registration and encryption |
GotenbergEnvVarDefinition | GOTENBERG_BASE_URL | Gotenberg PDF service URL (optional) |
The profile installs the following bundles during installation:
| Bundle | Purpose |
|---|---|
PimcoreGenericExecutionEngineBundle | Generic execution engine for background jobs |
PimcoreCustomReportsBundle | Custom reporting engine |
PimcoreGenericDataIndexBundle | Generic data indexing |
PimcoreStudioBackendBundle | Studio backend API |
PimcoreStudioUiBundle | Studio UI frontend |
PimcoreEnterpriseSubscriptionToolsBundle | Enterprise subscription tools |
Skipped install steps on PaaS
The PaaS profile implements InstallStepFilterInterface to skip install steps that are already handled by the PaaS deployment pipeline:
| Skipped Step | Reason |
|---|---|
WriteEnv | Environment variables are provided by the platform via PLATFORM_RELATIONSHIPS and startup.php — no .env.local file is needed |
WriteDoctrineConfig | Doctrine mapping types configuration is provided by the platform.yaml template |
RegisterBundles | config/bundles.php is committed in the repository — the installer does not need to write to it |
InstallAssets | Assets are installed during the build phase via assets:install in the build hook |
RunMaintenance | Maintenance is handled by the pimcore_maintenance cron job |
This eliminates the need for filesystem workarounds (symlinks, writable mounts for config files) that were previously required to allow the installer to write to the read-only deploy filesystem.
How it runs on PaaS
During the first deployment, check-installation-status.sh runs:
php vendor/bin/pimcore-install --install-profile='Pimcore\Paas\Install\PaasProfile' --no-interaction
Since startup.php has already populated all env vars from PLATFORM_RELATIONSHIPS at this point, the installer runs non-interactively — it reads DATABASE_URL, PIMCORE_OPENSEARCH_DSN, PIMCORE_MESSENGER_TRANSPORT_DSN_PREFIX, etc. directly from the environment without prompting.
Using the profile locally
You can use the same profile for local installation:
./vendor/bin/pimcore-install --install-profile='Pimcore\Paas\Install\PaasProfile'
The installer will interactively prompt for each service connection. To skip validation for services you don't run locally:
./vendor/bin/pimcore-install --install-profile='Pimcore\Paas\Install\PaasProfile' --skip-validation=gotenberg
Using a custom profile
If your project needs additional env var definitions or bundles beyond what the PaaS profile provides, create your own profile class implementing InstallProfileInterface and reference it in .platform-scripts/deploy/check-installation-status.sh:
namespace App\Install;
use Pimcore\Bundle\InstallBundle\Profile\InstallProfileInterface;
use Pimcore\Bundle\InstallBundle\Profile\InstallStep;
use Pimcore\Bundle\InstallBundle\Profile\InstallStepFilterInterface;
final readonly class MyProjectProfile implements InstallProfileInterface, InstallStepFilterInterface
{
public function getSkippedInstallSteps(): array
{
return [
InstallStep::WriteEnv,
InstallStep::WriteDoctrineConfig,
InstallStep::RegisterBundles,
InstallStep::InstallAssets,
InstallStep::RunMaintenance,
];
}
// ... implement remaining InstallProfileInterface methods
}
Then update the deploy script:
php vendor/bin/pimcore-install --install-profile='App\Install\MyProjectProfile' --no-interaction
RabbitMQ
RabbitMQ is configured as the default transport for Symfony messenger. If you would like to change that, you need to update the following files and remove the RabbitMQ config and dependencies:
config/pimcore/startup.phpconfig/packages/messenger.yaml.platform/applications.yaml.platform/services.yamlcomposer.json
OpenSearch
For starting a new project based on Enterprise demo, the default configuration on the Pimcore side is done for OpenSearch.
If you are migrating your project and want to use OpenSearch, uncomment the following block in config/platform.yaml:
pimcore_open_search_client:
clients:
default:
dsn: '%env(PIMCORE_OPENSEARCH_DSN)%'
S3 buckets storage
We support S3 buckets as storage. Default configuration is provided by Pimcore.
If you would like to use S3 buckets as storage, you need to uncomment the assets_s3 service definition and the flysystem storage blocks in config/platform.yaml, and add the following variables to your project/environment in the Pimcore PaaS Console:
env:S3_STORAGE_KEY- with the access keyenv:S3_STORAGE_SECRET- with the secretenv:S3_PRIVATE_BUCKET- with the name of the bucket which will contain private filesenv:S3_PUBLIC_BUCKET- with the name of the bucket which will contain publicly available files
S3 bucket policies
Your bucket policies could be configured as following:
- Public files
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::id:user/username"
},
"Action": [
"s3:ListBucket",
"s3:GetObject",
"s3:PutObject",
"s3:ReplicateObject",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::pimcore-public/*",
"arn:aws:s3:::pimcore-public"
]
},
{
"Sid": "PublicRead",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::pimcore-public/*"
}
]
}
- Private files
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::id:user/username"
},
"Action": [
"s3:ListBucket",
"s3:GetObject",
"s3:PutObject",
"s3:ReplicateObject",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::pimcore-private/*",
"arn:aws:s3:::pimcore-private"
]
}
]
}
You could also combine Cloudfront with your S3 config to optimize performance and costs of an S3 bucket. Check the guide
Sitemaps configuration
By default sitemaps configs are commented out.
If you would like to enable it, uncomment it within applications.yaml under mounts and web configuration.
DataHub Configuration
ProductSup Export Cron Job
Enable the DataHub ProductSup cron job to process exports regularly:
datahub_product_sup:
spec: "* * * * *"
commands:
start: "php $PLATFORM_APP_DIR/bin/console datahub:export:productsup -p 1 --only-queue-items"
The same applies to other processes like datahub:simple-rest:process-queue:
datahub:simple-rest:process-queue:
spec: "* * * * *"
commands:
start: "php $PLATFORM_APP_DIR/bin/console datahub:simple-rest:process-queue -p 1 --only-queue-items --batch-size=20"
Processes using this parallelization mechanism that should be considered:
pimcore:thumbnails:imagepimcore:thumbnails:videoecommerce:indexservice:bootstrapecommerce:indexservice:process-preparation-queueecommerce:indexservice:process-update-queuedatahub:simple-rest:process-queuedatahub:data-importer:process-queue-paralleldatahub:export:productsupdata-sync:export:batch-exportdata-sync:export:transfer-export-queuedata-sync:sync:deletionsdata-sync:import:execute-import-task
IMPORTANT!
It is possible that the list of commands which utilize parallelization is incomplete in the docs, so if you are experiencing low throughput on a command you should check whether -p is available.
Process Configuration (-p and --batch-size parameters):
- Use default:
-p 1(single process) - Use default
--batch-size=20so that command processes items in groups of 20, allowing intermediate cleanup and resource release between batches - For higher throughput: increase to e.g.
-p 2depending on your export volume - Consider your container resources when increasing the process count as it can affect performance
Where applicable, consider using --main-process instead of -p 1. While -p 1 limits execution to a single worker while keeping the parallelization mechanism available, --main-process completely disables parallel execution.
Workflow Designer
There is a limitation with workflows due to the functionality of the Symfony Workflow component. The component only supports Symfony Config, which prevents setting the configuration via LocationAwareConfigRepository. For more information, see the Pimcore documentation on configuration storage locations
We recommend to create workflows using the Workflow Designer locally and put the generated workflow configuration file(s) into the ie. '%kernel.project_dir%/config/workflows/workflows.yaml' and add them to Git.
To do so the following change in the configuration is necessary:
Add an import statement in the /config/config.yaml file:
imports:
- { resource: 'workflows/' }
Ensure that your workflow YAML file(s) is placed in the /config/workflows/ directory.
By following these steps, the workflow configuration process should work smoothly.
Large asset uploads
Several parameters influence the maximum size of assets you can upload. They operate at different layers (router vs. PHP), and the smallest limit will always win.
Upsun router limit
max_request_size
Default: 250 MB
This is the maximum HTTP request size accepted by the Upsun router (mapped to nginx’sclient_max_body_size).
When you encounter a413 Request Entity Too Large, this limit was exceeded.
See: Upsun Docs
PHP limits
-
upload_max_filesize
Default: 2 MB
Maximum size of a single uploaded file. -
post_max_size
Default: 8 MB
Maximum size of the entire POST body, including all files, fields, and multipart overhead. -
max_file_uploads
Default: 20
Maximum number of files allowed in one request.
Required hierarchy
To avoid unexpected rejections at different layers, ensure:
max_request_size > post_max_size > upload_max_filesize
About memory_limit
memory_limit does not cap upload size directly, since PHP streams uploaded files to temporary storage on disk.
However, PHP still needs sufficient memory to parse the POST body and populate $_POST/$_FILES.
Because post_max_size defines the largest payload PHP will process, memory_limit should always be greater than post_max_size to prevent fatal “Allowed memory size exhausted” errors.
How to override PHP limits on PaaS
You can override PHP settings in .platform/applications.yaml under the variables.php section of your app definition (e.g., pimcore:). For example, to allow uploading files up to 256 MB:
pimcore:
variables:
php:
upload_max_filesize: 256M
post_max_size: 300M
memory_limit: 512M
max_file_uploads: 50