Configure TYPO3 for Platform.sh

The end goal of this guide is to deploy your TYPO3 app to a project on Platform.sh. In many ways, a project is just a collection of tools around a Git repository. A project replicates the branching structure of a repository exactly, but with one important addition: any branch can be activated to become an environment on Platform.sh. Activated environments go through Platform.sh’s build and deploy phases, resulting in a fully isolated running site for each activated branch (or pull request) on that repository.

Once an environment is activated, Platform.sh provisions a cluster of containers to deploy your app. The configuration of that cluster is controlled by three YAML files:

  • .platform/routes.yaml controls how incoming requests are routed to your app, or apps in a multi-app setup. It also controls the built-in HTTP cache. If you’re only using the single default route, you don’t need this file.
  • .platform/services.yaml controls what additional services are created to support your app, such as databases or search servers. Each environment has its own independent copy of each service. If you’re not using any services, you don’t need this file.
  • .platform.app.yaml controls the configuration of the container where your app lives. It’s the most powerful configuration file with the most options. So it can get somewhat long depending on your configuration.

Each project on Platform.sh needs at least the last file and each file can be customized however you need. But most TYPO3 sites have a fairly similar configuration, at least to start.

You can start by creating empty versions of each of these files in your repository:

# Create empty Platform.sh configuration files
touch .platform.app.yaml && mkdir -p .platform && touch .platform/routes.yaml

Now that you’ve added these files to your project, you can go through and configure each of them for TYPO3 one by one in the sections below. Each section covers a particular configuration file, defines what each attribute configures, and then shows a final code snippet that includes the recommended configuration for TYPO3 pulled from its template. Within that snippet, be sure to read each of the comments as they provide additional information and reasoning for why TYPO3 requires those values.

Requests configuration: routes.yaml 

The routes.yaml file controls the routing and caching for all HTTP requests sent to your app. Typically you just route all incoming requests to your one app container, where your site lives, but many more elaborate configurations are possible.

The two most important parts to configure are the main route itself and its caching rules. A route can have a placeholder of {default}, which is replaced with a branch-specific generated domain name or, in production, your configured domain name.

The route then has an upstream, which is the name of the container that it should forward requests to. Most of the time, you want your app’s name.

You can (and should) enable the HTTP cache. The router includes a basic HTTP cache that obeys the HTTP cache headers produced by your app. However, by default HTTP caches includes all cookies in the cache key. So if you have any cookies at all, you can’t cache the site. The cookies key allows you to select which cookies should matter for the cache Generally, you just want the user session cookie, which is included in the example for TYPO3. You may need to add other cookies depending on what additional modules you have installed.

Routes can also be HTTP redirects, either fully or partially. In the following example, all requests to www.{default} are redirected to the equivalent URL without www. You could configure it the other way around if you want. More complex redirects are also possible.

Don’t worry about unencrypted HTTP routes. All requests on Platform.sh are TLS-enabled and HTTP requests are automatically redirected to HTTPS.

If you don’t include a routes.yaml file, a single default route is deployed. This is equivalent to the following:

.platform/routes.yaml
https://{default}/:
  type: upstream
  upstream: <APP_NAME>:http

Where <APP_NAME> is the name you’ve defined in your app configuration.

You can also create other routes as you like:

# This file describes an application's routes. You can define as many routes as your
# application requires.
#
# See https://docs.platform.sh/configuration/routes.html

# URL template for the main route, where `{default}` is replaced by
# the Platform.sh generated environment URL or the configured domain.
"https://{default}/":
    # This route serves an application (upstream).
    type: upstream

    # Defines the upstream according to the form `<application-name>:http`,
    # where the `name` attribute from `.platform.app.yaml` is used.
    upstream: "app:http"

    # Optional route identifier; constant across all environments. 
    # 
    # For TYPO3, the id "main" is used within `public/typo3conf/PlatformshConfig.php` 
    # to set the `base` attribute for the site (in `config/sites/main/config.yaml`) from the 
    # Platform.sh environment variable `PLATFORM_ROUTES`.
    id: "main"

    # HTTP cache configuration.
    cache:
        # Enabled (default `true`, but only if `cache` attribute is unspecified).
        enabled: true

        # List of allowed cookie names, all others are ignored. 
        cookies:
          # Used to identify a backend session when Backend User logged in.
          - 'be_typo_user'
          # Used to identify session ID when logged into TYPO3 Frontend.
          - 'fe_typo_user'
          # Default PHP session cookie.
          - 'PHPSESSID'
          # Validates sessions for System Maintenance Area.
          - 'Typo3InstallTool'

Service configuration: services.yaml 

The services.yaml file lists the pre-packaged services you need for your application to run. You pick the major version of the service and Platform.sh updates the patch version periodically so that you always get the newest version when you deploy.

We recommend the latest MariaDB version for TYPO3, although you can also use Oracle MySQL or PostgreSQL if you prefer. We also strongly recommend using Redis for TYPO3 caching. Our TYPO3 template comes pre-configured to use Redis for caching.

You can add other services if desired, such as Solr or Elasticsearch. You need to configure TYPO3 to use those services once they’re enabled.

Each service entry has a name (db and cache in the example below) as well as a type that specifies the service and version to use. Note that not all services support clean version upgrades, and none support downgrades. If you want to try upgrading a service, confirm on its service page that it’s supported and test on a branch before pushing to your production branch.

If a service stores persistent data, then it also has a disk key, which specifies the amount of storage to give it, in MB.

# This file describes an application's services. You can define as many services as your
# application requires, subject to your plan's resource restrictions.
#
# See https://docs.platform.sh/configuration/services.html.

# MariaDB/MySQL 10.4 service with 2048MB of allocated disk.
# The service name `db` is used in defining the `database` relationship in the
# `.platform.app.yaml` file.
# 
# See https://docs.platform.sh/configuration/services/mysql.html.
db:
    type: mysql:10.4
    disk: 2048

# Redis 5.0 service definition. 
# The service name `cache` is used in defining the `rediscache` relationship in the
# `.platform.app.yaml` file.
# 
# See https://docs.platform.sh/configuration/services/redis.html.
cache:
    type: redis:5.0

Application container: .platform.app.yaml 

The .platform.app.yaml file is the heart of your configuration. It has an extensive set of options that allow you to configure nearly any aspect of your app. Most of it is explained with comments inline. This file changes over time as you build out your site.

Note that the command php vendor/bin/typo3cms install:generatepackagestate is run during the build hook. The command ensures all installed extensions are enabled and that they can be omitted if you commit your own PackageStates.php file.

# This file describes an application. You can have multiple applications
# in the same project.
#
# See https://docs.platform.sh/configuration/app.html

# The name of this app. Must be unique within a project.
name: app

# The runtime the application uses.
type: php:7.4

dependencies:
    php:
        composer/composer: '^2'

runtime:
    # Enable the redis extension so TYPO3 can communicate with the Redis cache.
    extensions:
        - redis

# Composer build tasks run prior to build hook, which runs
# composer --no-ansi --no-interaction install --no-progress --prefer-dist --optimize-autoloader
# if composer.json is detected.
build:
    flavor: composer

# The relationships of the application with services or other applications.
# The left-hand side is the name of the relationship as it will be exposed
# to the application in the PLATFORM_RELATIONSHIPS variable. The right-hand
# side is in the form `<service name>:<endpoint name>`.
#
# NOTE: Be sure to update database and Redis configuration in `public/typo3conf/PlatformshConfiguration.php`
# if you rename the relationships here.
relationships:
    # MariaDB/MySQL will then be accessible to the app from 'database' relationship.
    # The service name `db` must match the top-level attribute in `.platform/services.yaml`.
    database: 'db:mysql'

    # Redis will then be accessible to the app from 'rediscache' relationship.
    # The service name `cache` must match the top-level attribute in `.platform/services.yaml`.
    rediscache: 'cache:redis'

# The configuration of app when it is exposed to the web.
web:
    # How the application container responds to incoming requests.
    locations:
        # All requests not otherwise specified follow these rules.
        '/':
            # The folder from which to serve static assets, for this location.
            # This is a filesystem path, relative to the application root.
            root: 'public'

            # Redirect any incoming request to TYPO3's front controller.
            passthru: '/index.php'

            # File to consider first when serving requests for a directory.
            index:
                - 'index.php'

            # Deny access to all static files, except those specifically allowed below.
            allow: false

            # Rules for specific URI patterns.
            rules:
                # Allow access to common static files.
                '\.(jpe?g|png|gif|svgz?|css|js|map|ico|bmp|eot|woff2?|otf|ttf)$':
                    allow: true
                '^/robots\.txt$':
                    allow: true
                '^/sitemap\.xml$':
                    allow: true

        # Default Storage location where TYPO3 expects media resources to be located.
        # Writable at runtime with the mount `public/fileadmin`.
        '/fileadmin':
            root: 'public/fileadmin'
            # Do not execute PHP scripts from the writeable mount.
            scripts: false
            allow: true
            passthru: '/index.php'

        # Directory for temporary files that should be publicly available (e.g. generated images).
        # Writable at runtime with the mount `publi/typo3temp`.
        '/typo3temp/assets':
            root: 'public/typo3temp/assets'
            # Do not execute PHP scripts from the writeable mount.
            scripts: false
            allow: true
            passthru: '/index.php'

        # Local TYPO3 installation settings.
        '/typo3conf/LocalConfiguration.php':
            allow: false

        # Additional TYPO3 installation settings.
        '/typo3conf/AdditionalConfiguration.php':
            allow: false

# The size of the persistent disk of the application (in MB).
disk: 2048

# The 'mounts' describe writable, persistent filesystem mounts in the application.
mounts:
    # Directory for temporary files. It contains subdirectories (see below) for
    # temporary files of extensions and TYPO3 components.
    "public/typo3temp":
        source: local
        source_path: "typo3temp"
    # Default Storage location where TYPO3 expects media resources to be located.
    "public/fileadmin":
        source: local
        source_path: "fileadmin"
    # Contains system files, like caches, logs, sessions
    "var":
        source: local
        source_path: "var"

# The hooks that will be performed when the package is deployed.
hooks:
    # The build hook runs after Composer to finish preparing up your code.
    # No services are available but the disk is writeable.
    build: |
        # Exit hook immediately if a command exits with a non-zero status.
        set -e

        # Start the installation with no interaction or extension setup, using `SetupConfiguration.yaml`.
        if [ ! -f var/platformsh.installed ]; then
            php vendor/bin/typo3cms install:setup --install-steps-config=src/SetupConfiguration.yaml --no-interaction --skip-extension-setup
        fi;

        # Generates the `PackageStates.php` file from the `composer.json` configuration
        php vendor/bin/typo3cms install:generatepackagestates

        # Enable the install tool for 60mins after deployment.
        touch public/typo3conf/ENABLE_INSTALL_TOOL

        # Keep the checked-in LocalConfiguration available, but make the actual file writable later-on
        # by creating a symlink which will be accesible below.
        if [ -f public/typo3conf/LocalConfiguration.php ]; then
            mv public/typo3conf/LocalConfiguration.php public/typo3conf/LocalConfiguration.FromSource.php
            ln -sf ../../var/LocalConfiguration.php public/typo3conf/LocalConfiguration.php
        fi;

        # Clean up the FIRST_INSTALL file, that was created.
        if [ -f public/FIRST_INSTALL ]; then
            rm public/FIRST_INSTALL
        fi;

        # initial activation of Introduction package will fail if it is unable to write to this images directory.
        # if it exists, we'll move its contents out to a temp space, remove the original, and symlink to a writable mount
        if [ -d public/typo3conf/ext/introduction/Initialisation/Files/images/ ]; then
            if [ ! -d imagestemp ]; then
                # create our temp images directory
                mkdir -p imagestemp
                # copy the image files out of the real location into our temp space
                cp -r public/typo3conf/ext/introduction/Initialisation/Files/images/. imagestemp/
            fi

            #now create the symlink for the images
            #remove the original directory
            rm -rf public/typo3conf/ext/introduction/Initialisation/Files/images/
            # now create a symlink
            ln -sf "$PLATFORM_APP_DIR/var/images" public/typo3conf/ext/introduction/Initialisation/Files/images
        fi        

    # The deploy hook runs after your application has been deployed and started.
    # Code cannot be modified at this point but the database is available.
    # The site is not accepting requests while this script runs so keep it
    # fast.
    deploy: |
        # Exit hook immediately if a command exits with a non-zero status.
        set -e

        # if the images location existed in the build hook, it was converted to a symlink. we now to need to make sure
        # the target of the symlink exists, and then rsync any new files to the writable location
        if [ -L public/typo3conf/ext/introduction/Initialisation/Files/images ]; then
            #make sure our images directory exists in var mount
            if [ ! -d var/images ]; then
                mkdir -p var/images
                echo "This directory is symlinked to public/typo3conf/ext/introduction/Initialisation/Files/images/. Do not delete." >> var/images/readme.txt
            fi

            #rsync any new files from imagestemp into var/images
            rsync -a --ignore-existing imagestemp/ var/images
        fi

        # Set TYPO3 site defaults on first deploy.
        if [ ! -f var/platformsh.installed ]; then
            # Copy the created LocalConfiguration into the writable location.
            cp public/typo3conf/LocalConfiguration.FromSource.php var/LocalConfiguration.php

            # On first install, create an inital admin user with a default password.
            # *CHANGE THIS VALUE IMMEDIATELY AFTER INSTALLATION*
            php vendor/bin/typo3cms install:setup \
                --install-steps-config=src/SetupDatabase.yaml \
                --site-setup-type=no \
                --site-name="TYPO3 on Platform.sh" \
                --admin-user-name=admin \
                --admin-password=password \
                --skip-extension-setup \
                --no-interaction

            # Sets up all extensions that are marked as active in the system.
            php vendor/bin/typo3cms extension:setupactive || true

            # Create file that indicates first deploy and installation has been completed.
            touch var/platformsh.installed
        fi;        

crons:
     # Run TYPO3's Scheduler tasks every 5 minutes.
    typo3:
        spec: "*/5 * * * *"
        commands:
            start: "vendor/bin/typo3 scheduler:run"

source:
  operations:
    auto-update:
      command: |
                curl -fsS https://raw.githubusercontent.com/platformsh/source-operations/main/setup.sh | { bash /dev/fd/3 sop-autoupdate; } 3<&0