DEV Community

Matthias Andrasch
Matthias Andrasch

Posted on • Updated on

Statamic meets Hetzner Cloud, Ploi and Deployer

Enough with the adjustments, let's deploy this thing! 🚀

🚧 This article is work in progress. 🚧

I'll use the following combination for this:

  • (private) GitHub repository
  • DDEV for my local dev environment, see previous articles
  • Deployer for the actual deployment workflow
  • Hetzner Cloud Hosting
  • for help with setting up the cloud server and adding PHP projects

Register a Hetzner Cloud Account

Hetzner launched a flexible cloud service back in 2018, but I wasn't really aware of it. Why Hetzner? They offer servers in Germany (and other EU-locations) which is important to avoid GDPR-struggles. They are also listed in the Green Web Directory. 🌱

The CX11 entry plan starts with a monthly price cap of 4,15€ / month. If you use it less than a month and delete it before, there will be just a hourly rate billed based on your usage.

That is great for trying out stuff of course.

How do you bill your servers?
Servers have both a monthly price cap and a price per hour. Your server's bill will never exceed its monthly price cap. If you delete your Cloud Server before the end of the billing month, you will only be billed the hourly rate. We will bill you for each cloud server until you choose to delete them. Even if you aren't actively using your server, we will bill you for it.

Register an account at and create a API token for Ploi (see next step):

Screenshot API token Hetzner

Connect Ploi with Hetzner via API token is like your personal IT server administrator which helps you setting up and configuring the cloud servers. You could do this yourself as well if you're experienced in these (linux) things. I'm not really, so I'll use a service like Ploi, Laravel Forge, Cleavr, etc. ;-)

Ploi Screenshot add service provider

Create (provision) the first server

Now let's create our first server, I'll go with PHP 8.0 by now:

Ploi create a server

Ploi create a server settings

Ploi will now install and configure nginx webserver, database, Redis, Supervisor, PHP, Composer, Memcached, NPM (NodeJS), UFW Firewall, Fail2ban and basic packages on the new server. (See: - Server Installation.

Create a new site

We can add multiple sites to a server. Let's create one and select Statamic as project type:

Add site on ploi to server

Ploi offers test domains, so no need to directly connect your real URL:

Screenshot test domain button

Don't use 1-click and don't add a repo on Ploi!

We don't connect a Git repository on Ploi directly.

Image description

While this is also a very handy and convenient feature, we want to use a standardized and portable deployment solution via a Deployer script (which can be used on any host and it can also run in Github Actions).

Add Deployer script

Deployer is a nice option if your target production server supports composer, NodeJS and SSH. You can write your deploy routine in PHP (instead of fiddling around with Github Action pipeline commands) and test it beforehand locally.

The docs of Deployer are a little bit confusing, because simple and concrete examples are missing. But we will struggle through this together. First install Deployer to your Statamic project:

ddev composer require --dev deployer/deployer:^7
Enter fullscreen mode Exit fullscreen mode

Now we need to create our deploy.php file (you could also write a deploy.yaml file). Deployer v7 has already a Statamic recipe which can be imported and modified.

This is my current state for the deploy.php file:


namespace Deployer;

require 'contrib/npm.php';


set('application', 'My Statamic Site');

// The git repository which should be used
set('repository', '');

// Targets for deployment (SSH),
// this will be called via 'ddev dep deploy production'
    ->set('remote_user', 'ploi')
    ->set('hostname', '123.456.779.1')
    ->set('deploy_path', '~/');

// Sharead and writable directories
// (These will be kept on the server and will not be replaced on deployments)
add('shared_dirs', [
add('writable_dirs', [

// Npm production build
desc('Compile JS/SCSS via npm');
task('npm:production', function(){
    run('cd {{release_path}} && npm ci && npm run production');

// We copied the following from Statamic recipe to know what's going on
// (
 * Main Deploy Script for Statamic, which
 * will overwrite the Laravel default.
desc('Deploys your project');
task('deploy', [
    // TODO: good spot here?

after('deploy:failed', 'deploy:unlock');

Enter fullscreen mode Exit fullscreen mode

To use the dep cli command of Deployer in DDEV, we need a little custom command add-on:

ddev get mandrasch/ddev-deployer-dep
Enter fullscreen mode Exit fullscreen mode

Try Deployer workflow locally

Now we need to set the connection settings in deploy.php accordingly to the information shown in ploi, the user is always "ploi" and the IP address is shown in the dashboard:

    ->set('remote_user', 'ploi')
    ->set('hostname', '123.456.789.123')
    ->set('deploy_path', '~/');
Enter fullscreen mode Exit fullscreen mode

To connect to the server via SSH, you need to add your local public SSH key to your ploi profile:

Add SSH key to ploi

On Mac you'll get your public key via

cat ~/.ssh/
Enter fullscreen mode Exit fullscreen mode

Paste it in and send it to your servers:

Screenshot add ssh key to server

The important part now is to add your local SSH keys to your DDEV project containers as well. This is just a quick command:

ddev auth ssh
Enter fullscreen mode Exit fullscreen mode

Afterwards we can try our first Deployer run locally via DDEV:

ddev dep deploy production
Enter fullscreen mode Exit fullscreen mode

Deployer will connect to your target server via SSH, clone the repository from Github and run the rest of the routine described in deploy.php. For example Deployer will run the npm production build and setup the folder structure (all via SSH on the target server).

Add servers SSH key to GitHub

The SSH connection from DDEV to the server should work by now, but the private git repository can't be accessed (of course):

[production] Permission denied (publickey).
[production] fatal: Could not read from remote repository.
[production] Please make sure you have the correct access rights
Enter fullscreen mode Exit fullscreen mode

Therefore we need to add the SSH key of the target server (the ploi/hetzner server!) to Github.

We need to connect to the ploi server via ssh ploi@XXX.XXX.XXX and use the command cat ~/.ssh/ again. Add this key to the Deploy Keys of your private repository on

Screenshot Deploy Keys

You should now have a success moment after running ddev dep deploy production:

Screenshot command line output successful run

Now only the .env file must be configured. We can use SSH (or you could use an FTP program), the file is located in shared/.env.

Unfortunately - while I was testing the "Edit environment" feature of Plois Laravel Tab did not work with this Deployer setup. Make sure to set the project directory to current/ anyhow to use the artisan commands:

Image description

Show the live site!

One last thing to do, change the web directory to current/public, this is where the final site get's served from:

Screenshot change web directory

Find the "View test" link and open your site in the browser. Visit /cp and add a home page:

Screenshot add home page

Now you should have a functioning home page:

Screenshot Hello World place holder

Deployer folder structure

Deployer uses symlinks and configures the following structure for us, it's called zero downtime deployment because the new versions are switched in via symlink after they are ready:

Image description

The current/-folder:

Image description

The shared/-folder, this will be kept during deployment changes:

Image description

GitHub Action for Deployer

You might be wondering: Yeah, nice. But do I have to run the deploy every time locally? No, this is just for testing. Later in this series we can add the GitHub deploy action which runs on github (automatically for every commit in a certain branch)


Ploi has monitoring in the higher-priced subscription, but you can view the server load as well as server traffic in the Hetzner Cloud dashboard:

Screenshot hetzner cpu

Build everything in the pipeline instead?

There is a great tutorial for typo3: TYPO3 Gitlab CI/CD Auto Deployment (2022), the corresponding deploy.php can be found here. What is the advantage? Instead on running composer install and npm run production on the target server, everything is build beforehand in the Github/Gitlab/Bitbucket action pipeline and - only if successful - transferred via rsync to the server. I'm not completely sure yet but it seems to be a safer approach since the target server will be only 'touched' if the build steps before were 100% successful.

(There is also no git checkout anymore on the server itself and there is no need to add the Github SSH key to the target server.)

Hope I have time to integrate this approach for Statamic/Laravel as well.

More resources:

Coming soon - next articles:

Top comments (1)

mandrasch profile image
Matthias Andrasch

Update: If you run into "Error: key on use" with the second project, this tutorial is very helpful: