Every time you push to a live branch (a git branch with an active environment attached to it) or activate an environment for a branch, there are two main processes that happen: Build and Deploy.

  1. The build process looks through the configuration files in your repository and assembles the necessary containers.
  2. The deploy process makes those containers live, replacing the previous versions, with virtually no interruption in service.

Build pipeline

Always Be Compiling 

Interpreted languages like PHP or Node.js may not seem like they have to be compiled, but with modern package management tools like Composer and npm as well as the growing use of CSS preprocessors such as Sass, most modern web applications need a “build” step between their source code and their production execution code. The Platform.sh build step includes the entire application container (from language version to build tools to your code), rebuilt every time.

That build process is under your control and runs on every Git push. Every Git push is a validation not only of your code, but of your build process. Whether that’s installing packages using Composer or Bundler, compiling TypeScript or Sass, or building your application code in Go or Java, your build process is vetted every time you push.

Whenever possible, you should avoid committing build assets to your repository that can be regenerated at build time. Depending on your application, that may include third-party libraries and frameworks, generated and optimized CSS and JS files, and generated source code.

The following two constraints make sure you have fast, repeatable builds:

  1. The build step should be environment-independent. This is paramount to ensure development environments are truly perfect copies of production. This means you can not connect to services (like the database) during the build.
  2. The final built application must be read-only. If your application requires writing to the filesystem, you can specify the directories that require Read/Write access. These shouldn’t be directories that have code, because that would be a security risk.

Building the application 

After you push your code, the first build step is to validate your configuration files (.platform.app.yaml, .platform/services.yaml, and .platform/routes.yaml). The Git server issues an error if validation fails, and nothing happens on the server.

The live environment is composed of multiple containers—both for your applications and for the services it depends on. It also has a virtual network connecting them as well as a router to route incoming requests to the appropriate application.

Based on your application type, the system selects one of the pre-built container images and runs the following:

  1. First, any dependencies specified in the .platform.app.yaml file are installed. Those include tools like Sass, webpack, and Drupal Console.

  2. Then, depending on the build flavor specified in the configuration file, a series of standard commands is run. The default for PHP containers, for example, is running composer install.

  3. Finally, the build hook from the configuration file is run. The build hook comprises one or more shell commands that you write to finish creating your production code base. That could be compiling Sass files, running a bundler, rearranging files on disk, compiling an application in a compiled language, or whatever else you want. Note that, at this point, all you are able to access is the file system; there are no services or other databases available.

Once all of that is completed, we freeze the file system and produce a read-only container image. That image is the final build artifact: a reliable, repeatable snapshot of your application, built the way you want, with the environment you want.

Because container configuration (for both your application and its underlying services) is exclusively based on your configuration files and your configuration files are managed through Git, we know that a given container has a 1:1 relationship with a Git commit. That means builds are always repeatable. It also means that if we detect that there are no changes that would affect a given container, we can skip the build step entirely and reuse the existing container image, saving a great deal of time.

In practice, the entire build process usually takes less than a minute.

Deploying the application 

Deploying the application also has several steps, although they’re much quicker.

First, we pause all incoming requests and hold them so that there’s no interruption of service. Then we disconnect the current containers from their file system mounts, if any, and connect the file systems to the new containers instead. If it’s a new branch and there is no existing file system, we clone it from the parent branch.

We then open networking connections between the various containers, but only those that were specified in the configuration files (using the relationships key). No configuration, no connection. That helps with security, as only those connections that are actually needed even exist. The connection information for each service is available in an application as environment variables.

Now that we have working, running containers there are two more steps to run. First, if there is a start command specified in your .platform.app.yaml file to start the application, we run that. (With PHP, this is optional.)

Then we run your deploy hook. Just like the build hook, the deploy hook is any number of shell commands you need to prepare your application. Usually this includes clearing caches, running database migrations, and so on. You have complete access to all services here as your application is up and running, but the file system where your code lives is now read-only.

Finally, once the deploy hooks are done, we open the floodgates and let incoming requests through to your newly deployed application. You’re done!

In practice, the deploy process takes only a few seconds, plus whatever time is required for your deploy hook, if any.