Compiling a Drupal site with Tome

Goal

To demonstrate how a “compile to static” tool, such as the Drupal Tome module, can be used on Platform.sh using multi-application configurations.

Assumptions

  • Your default/production git branch is named main

Preparation

This tutorial will start from a basic Drupal 8 Composer site. However, it will be easier to start from an empty Git repository. It can be done from a Drupal 8 template site but that requires moving a number of hidden files and is therefore more prone to error.

You will need:

  • A newly created empty site on Platform.sh.
  • Git and SSH installed locally.

Problems

How can you serve two closely related sites off of the same project?

How can static site generation work in production, rather than compiling locally or during build?

Steps

1. Create an empty working directory

On your local system, create an empty directory for your project and initialize it for Git. Eg:

mkdir project
cd project
git init

2. Download Drupal 8

Clone the Drupal 8 Platform.sh template into your project, then remove the git repository from it.

git clone https://github.com/platformsh-templates/drupal8.git drupal --depth=1
rm -rf drupal/.git

That Git repository is what the Drupal 8 template in the new project wizard populates from. The above commands are essentially what the new project wizard does, although it does not use a subdirectory.

2. Move the .platform definition files to the repo root

The .platform directory must be in the repository root, so move it there:

mv drupal/.platform .

The .platform.app.yaml file stays in the drupal directory so that the drupal directory becomes an application container itself. That will let you create another directory for the static site.

3. Add a network-storage service

Open .platform/services.yaml in your favorite text editor and add the following:

files:
    type: network-storage:1.0
    disk: 512

That creates a new network-storage service named files. Files there can be shared between the two application containers.

4. Update Drupal mounts

Open drupal/.platform.app.yaml in your favorite text editor and update the mounts section to be as follows:

mounts:
    '/web/sites/default/files':
        source: local
        source_path: files
    '/tmp':
        source: local
        source_path: tmp
    '/private':
        source: local
        source_path: private
    '/.drush':
        source: local
        source_path: drush
    '/drush-backups':
        source: local
        source_path: drush-backups
    '/.console':
        source: local
        source_path: console
    '/content':
        source: local
        source_path: content
    '/config':
        source: local
        source_path: config
    '/static':
        source: service
        service: files
        source_path: static

Most of those are simply converting the normal Drupal mounts to the new-style mount syntax. The most important are the final three paths. Tome will automatically run a config-export every time configuration is exported, so the /config directory must be writeable. It will also write out a JSON version of all content as it is created and edited to /content. The /static directory is where you’ll instruct Tome to write out the static version of the site.

Of particular note, the /static mount uses the network storage service, not the local file system. That will make it available to other application containers.

Note: Because these directories are writeable, they will not be tracked in Git. That includes configuration exports, which will not be deployable via Git in this setup.

5. Install Drupal Tome module

Run:

cd drupal
composer require drupal/tome

to add the Tome module. (It’s actually a suite of modules). You’ll enable it later once the code is deployed. Once that’s done return to the repository root directory:

cd ..

6. Set Tome export location

Modify the drupal/web/sites/default/settings.php file. Add the following line at the location of your choice:

$settings['tome_static_directory'] = '../static/html';

That will set the Tome export directory to the /static/html directory, which is inside the writeable mount you created above. Because Tome completely removes the output directory before each rebuild it needs to be in a subdirectory of the mount.

7. Disable allowed-host checking

In the drupal/web/sites/default/settings.platformsh.php file, look for a comment line that reads // Set trusted hosts based on Platform.sh routes. The code block below that configures the hosts that Drupal is allowed to requests for. Normally it is a security feature, however, on Platform.sh Professional the domain is already mangled by the router and will always be a “safe” domain. That makes this code block unnecessary. (It is included in the template only for Platform.sh Enterprise.)

The Tome module’s export functionality, however, is incompatible with this code block as it will need to use the un-prefixed domain for the export. It is safe to simply remove/comment out this entire block. Alternatively, just comment out the last line that sets $settings['trusted_host_patterns'].

8. Add a static site application to serve files

Create a new directory called static, and inside it create a .platform.app.yaml file. Give it the following contents:

# Note that this is different than what the Drupal site is called.
name: 'static'

# The type here doesn't really matter since it's just a static site.
type: 'php:7.3'

# This must be set, but can be set at the minimum value.
disk: 16

mounts:
    'static':
        source: service
        service: files
        source_path: static

web:
    locations:
        '/':
            root: 'static/html'
            index: ['index.html']
            scripts: false
            allow: true

That file is all that is needed to create a second application container. This container will be called static, and will mount the same network storage directory as the app container that holds the Drupal site. It will be empty aside from that mount, which is fine as the mount is where the files will be served from. Note that the web root is the static/html directory, not static itself, as Tome was configured above to write files to static/html.

9. Update routes

Open .platform/routes.yaml in your editor. Update the route definitions to be as follows:

"https://drupal.{default}/":
    type: upstream
    upstream: "app:http"
    cache:
        enabled: true

        # Base the cache on the session cookie and custom Drupal cookies. Ignore all other cookies.
        cookies: ['/^SS?ESS/', '/^Drupal.visitor/']

"https://{default}/":
    type: upstream
    upstream: "static:http"

(You can also include a www redirect route if you want, but that is unnecessary.) This configuration creates two routes: The drupal. subdomain will be where the Drupal site lives, and it will run as any other Drupal site. The main domain (and/or www. subdomain if desired) will be served by the static application.

Once the configuration is complete and you are happy with the result, you will most likely want to come back and add HTTP caching to the static route, including a default_ttl.

10. Commit and push

Commit all of the files you just created to Git:

git add .
git commit -m "Setting up Tome"

Now add a remote for your empty Platform.sh project. The Git URL can be found in your Management Console in the browser. The command will be something along the lines of:

git remote add platform bzh2mp6iabike@git.eu-3.platform.sh:bzh2mp6iabike.git

And then push all of the code to the platform remote:

git push -u platform main

If everything is setup correctly it will push and deploy a two-application cluster, containing the Drupal container, static container, and the MariaDB and Redis services used by Drupal itself.

11. Install Drupal and Tome

Once deployment is finished it will show you the domain names that are served. Go to the drupal. domain in your browser. Complete the Drupal installation process as normal.

Once Drupal is installed, select “Extend” in the menu and enable the “Tome” module. That will also enable several sub-modules. You don’t need to create any content at this point, but if you do it will make the demonstration of Tome’s functionality more interesting.

12. Export the site

Go to /admin/config/tome/static/generate in your Drupal site. The generation form will ask you for the domain to generate for; give it the unprefixed domain (which could be a dev domain or your production domain, depending on whether or not you’ve gone live yet). Click “Submit”.

Tome will generate a static version of your site as it is seen by an anonymous user. (Which means you must allow anonymous user to see the site.) The process takes anywhere from a few seconds to a few minutes depending on how much content you have. As the site was only just created it should be quite fast.

Now go to the unprefixed version of your site (the domain without drupal.) in your favorite browser. You should see the anonymous version of your site, now served entirely as static files.

Conclusion

In this tutorial, you have seen how to:

  • Create a multi-application project on Platform.sh.
  • Use network-storage to share content between sites.
  • Configure an application to serve static files.

All of which can be applied to other configurations if needed. Note that in the case of Tome the generation process does delete files first, so it is recommended to configure caching at the router level for the static container to minimize any disruption to the site as it is being regenerated.