Deploying PHP applications with Deployer
Deployer is a deployment tool for PHP applications. This guide will show you how to deploy a PHP application to a mittwald cloud project with Deployer.
Prerequisites
For this guide, we assume that you already have a PHP/Composer project in a Git repository that you want to deploy, and that you are familiar with the basics of Deployer (see the getting started guide, otherwise).
Deploy an application with Deployer
Create the application in the mittwald platform
First, you need to create a new custom application in a mittwald cloud project of your choice. You can do this via the mittwald Studio UI, or alternatively using the CLI. In any case, set the application's document root to the /current
symlink (or a subdirectory thereof) that will be created by Deployer.
$ # use one of these:
$ mw app create php --document-root /current/public
$ mw app create node --document-root /current
$ mw app create static --document-root /current/public
Make sure to take note of the application's ID, as you will need it later. In the following examples, we will refer to that application ID as <app-id>
.
Configure your deployment
Fully managed: Using the mittwald deployer recipe
The easiest way to deploy your application to a mittwald cloud project is to use the mittwald deployer recipe. To do so, install the recipe via Composer:
$ composer require --dev mittwald/deployer-recipes
To define the deployment target in your application, include the recipe in your deploy.php
file, and use the mittwald_app
function to define a host:
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/vendor/mittwald/deployer-recipes/recipes/deploy.php';
mittwald_app('<app-id>')
->set('public_path', '/');
When running dep deploy
, make sure that a MITTWALD_API_TOKEN
environment variable is set, containing a valid API token for the mittwald API.
The recipe will automatically configure the following things:
- The
deploy_path
will be set to the application's installation path. - An SSH user will be created for the application, and the
remote_user
will be set to that user. By default, the SSH key configured in thessh_copy_id
variable will be used for authentication. To use a different SSH key pair, set themittwald_ssh_public_key
andmittwald_ssh_private_key
variables (alternative, set themittwald_ssh_public_key_file
andmittwald_ssh_private_key_file
variables to the path of the respective files). - The PHP OPcache will automatically be cleared after deployment.
Alternative: Without the mittwald deployer recipe
After creating the application, retrieve its installation path from the mittwald Studio UI, or via the CLI (in the following examples, we will refer to that installation path as <app-installation-path>
).
$ mw app get <app-id>
Use the app installation directory as the deploy_path
in your deploy.php
file. For example:
host('ssh.fiestel.project.host') // you can determine your SSH host via the "mw project get" command
->set('remote_user', 'ssh-XXXXXX@<app-id>')
->set('deploy_path', '/html/<app-installation-path>');
To flush the OPcache, you can add your own task definition to your deploy.php
file, to be executed after the deploy:symlink
stage. The following example uses the CacheTool to selectively flush the OPcache for the application:
task("opcache:flush", function (): void {
if (!test("[ -x cachetool.phar ]")) {
run("curl -sLO https://github.com/gordalina/cachetool/releases/latest/download/cachetool.phar");
run("chmod +x cachetool.phar");
}
run('./cachetool.phar opcache:invalidate:scripts --fcgi=127.0.0.1:9000 {{ deploy_path }}');
});
after("deploy:symlink", "opcache:flush");
The rest of your Deployer configuration depends on your project, and for this reason, will not be covered by this guide. For example, if you are deploying a TYPO3 project, you might want to use the TYPO3 Deployer recipe.
Advanced recipe how-tos
The mittwald deployer recipe provides some additional features that might be useful for your deployment.
Deploying to multiple environments
Often, you will want to deploy your application to multiple environments, e.g. a staging and a production environment. To do so, you can create multiple apps on the mittwald platform:
$ mw app create php \
--document-root /current/public \
--site-title "My project (PROD)"
$ mw app create php \
--document-root /current/public \
--site-title "My project (STAGING)"
You can then define multiple hosts in your deploy.php
file, and set the mittwald_app_id
variable for each host:
mittwald_app('<prod-app-id>', hostname: 'mittwald-prod')
->set('branch', 'main');
mittwald_app('<staging-app-id>', hostname: 'mittwald-staging')
->set('branch', 'develop');
Configuring domains
The recipe will respect the domain
setting of your application, and will automatically look for a matching virtual host configured for your project. If a matching virtual host is found, the recipe will automatically link the virtual host with your application and its document root. You can also override the domain name(s) by setting the mittwald_domains
variable:
// default:
// set('mittwald_domains', ['{{domain}}']);
set('mittwald_domains', ['mittwald.example', 'www.mittwald.example']);
Configuring PHP version and other dependencies
You can also set the mittwald_app_dependencies
value. This value may contain an associative list of system software names and versions that your application depends on. The recipe will automatically install the required software packages in the application's runtime environment. For example:
set('mittwald_app_dependencies', [
'php' => '~8.2',
'composer' => '>=2.0',
]);
CI integration
Github Actions
To use this recipe in a Github actions workflow, you should first configure the following secrets in your repository settings:
MITTWALD_API_TOKEN
should contain your mittwald API tokenMITTWALD_APP_ID
should contain the ID of the mittwald application you want to deploy to.MITTWALD_SSH_PRIVATE_KEY
should contain the private key of the SSH key pair that should be used for deployment.MITTWALD_SSH_PUBLIC_KEY
should contain the public key of the SSH key pair that should be used for deployment.
Then, you can use the following workflow to deploy your application:
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.2
- name: Install dependencies
run: composer install --prefer-dist --no-progress --no-suggest
- name: Deploy SSH keys
env:
MITTWALD_SSH_PRIVATE_KEY: ${{ secrets.MITTWALD_SSH_PRIVATE_KEY }}
MITTWALD_SSH_PUBLIC_KEY: ${{ secrets.MITTWALD_SSH_PUBLIC_KEY }}
run: |
mkdir -p .mw-deploy
echo "${MITTWALD_SSH_PRIVATE_KEY}" > .mw-deploy/id_rsa
echo "${MITTWALD_SSH_PUBLIC_KEY}" > .mw-deploy/id_rsa.pub
chmod 600 .mw-deploy/id_rsa*
# NOTE: If you are NOT using the mittwald deployer recipe, you need add the SSH
# host key to the known hosts file here.
# See the "Host key verification failed" section below for more information.
- name: Run deployer
run: |
./vendor/bin/dep deploy \
-o mittwald_app_id={{ secrets.MITTWALD_APP_ID }} \
-o mittwald_ssh_public_key_file=.mw-deploy/id_rsa.pub \
-o mittwald_ssh_private_key_file=.mw-deploy/id_rsa
env:
MITTWALD_API_TOKEN: ${{ secrets.MITTWALD_API_TOKEN }}
Gitlab CI
This Gitlab CI workflow uses the same repository variables as the Github actions example above:
deploy:
image: php:8.2-cli
stage: deploy
before_script:
- wget https://raw.githubusercontent.com/composer/getcomposer.org/76a7060ccb93902cd7576b67264ad91c8a2700e2/web/installer -O - -q | php -- --quiet
- apt-get update && apt-get install -y git openssh-client
- mkdir -p .mw-deploy
- echo "$MITTWALD_SSH_PRIVATE_KEY" > .mw-deploy/id_rsa
- echo "$MITTWALD_SSH_PUBLIC_KEY" > .mw-deploy/id_rsa.pub
- chmod 600 .mw-deploy/id_rsa*
# NOTE: If you are NOT using the mittwald deployer recipe, you need add the SSH
# host key to the known hosts file here.
# See the "Host key verification failed" section below for more information.
script:
- |
./vendor/bin/dep deploy \
-o mittwald_app_id=$MITTWALD_APP_ID \
-o mittwald_ssh_public_key_file=.mw-deploy/id_rsa.pub \
-o mittwald_ssh_private_key_file=.mw-deploy/id_rsa
environment:
name: production
Common issues
unix_listener: path "[...]" too long for Unix domain socket.
This issue is caused by Deployers SSH multiplexing feature -- or more precisely, by the name of the generated UNIX socket, which is based on the host name and username and might be too long in some cases. There are several different workarounds for this issue:
-
Disable SSH multiplexing by setting
ssh_multiplexing
tofalse
in yourdeploy.php
file. -
Define a shorter host name in your SSH configuration (usually
~/.ssh/config
). Such a configuration might look like this:Host fiestel
Hostname ssh.fiestel.project.host
User ssh-XXXXXX@<app-id>
Host key verification failed
This issue is caused by the SSH client not being able to verify the host key of the mittwald platform. To fix this issue, you can add the mittwald platform's host key to your known hosts file. You can do this by running the following command on your local machine (replace [hostname]
with the actual SSH hostname):
$ ssh-keyscan [hostname] >> ~/.ssh/known-hosts
Alternatively, you can configure your SSH client to automatically accept unknown host keys by setting the StrictHostKeyChecking
option to accept-new
in your SSH configuration file (usually ~/.ssh/config
).
$ echo "StrictHostKeyChecking accept-new" >> ~/.ssh/config
From a security perspective, it is recommended to add the host key ahead-of-time, as described in the first solution. In a CI environment, you can add the host key statically as a secret or environment variable, and then write it to the known hosts file before running the deployment:
- GitHub Actions
- GitLab CI
First, configure the SSH host key as a secret for your repository. Using the GitHub CLI, you can do this with the following command (replace [hostname]
with your actual SSH hostname):
$ ssh-keyscan [hostname] | gh secret set MITTWALD_SSH_HOST_KEY -a actions
After that, add the following step to your GitHub Actions pipeline:
- name: Deploy SSH host key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.MITTWALD_SSH_HOST_KEY }}" > ~/.ssh/known-hosts
First, configure the SSH host key as a secret for your repository. Using the Gitlab CLI, you can do this with the following command (replace [hostname]
with your actual SSH hostname):
$ ssh-keyscan [hostname] | glab variable set MITTWALD_SSH_HOST_KEY
After that, add the following step to your Gitlab CI pipeline:
deploy:
image: php:8.2-cli
stage: deploy
before_script:
# [...]
- echo "$MITTWALD_SSH_HOST_KEY" > ~/.ssh/known-hosts