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
- Ploi.io 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 hetzner.com/cloud and create a API token for Ploi (see next step):
Connect Ploi with Hetzner via API token
Ploi.io 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. ;-)
Create (provision) the first server
Now let's create our first server, I'll go with PHP 8.0 by now:
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: ploi.io - Server Installation.
Create a new site
We can add multiple sites to a server. Let's create one and select Statamic as project type:
Ploi offers test domains, so no need to directly connect your real URL:
Don't use 1-click and don't add a repo on Ploi!
We don't connect a Git repository on Ploi directly.
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
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:
<?php
namespace Deployer;
require 'contrib/npm.php';
// https://deployer.org/docs/7.x/contrib/npm
import('recipe/statamic.php');
// https://deployer.org/docs/7.x/recipe/statamic
set('application', 'My Statamic Site');
// The git repository which should be used
set('repository', 'git@github.com:mandrasch/my-statamic-site.git');
// Targets for deployment (SSH),
// this will be called via 'ddev dep deploy production'
host('production')
->set('remote_user', 'ploi')
->set('hostname', '123.456.779.1')
->set('deploy_path', '~/my-statamic-site.mandrasch.dev');
// Sharead and writable directories
// (These will be kept on the server and will not be replaced on deployments)
add('shared_dirs', [
'content',
'storage'
]);
add('writable_dirs', [
'content',
'storage'
]);
// 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
// (https://deployer.org/docs/7.x/recipe/statamic)
/*
* Main Deploy Script for Statamic, which
* will overwrite the Laravel default.
*/
desc('Deploys your project');
task('deploy', [
'deploy:prepare',
'deploy:vendors',
// TODO: good spot here?
'npm:production',
'artisan:storage:link',
'artisan:cache:clear',
'statamic:stache:clear',
'statamic:stache:warm',
'deploy:publish',
]);
after('deploy:failed', 'deploy:unlock');
To use the dep
cli command of Deployer in DDEV, we need a little custom command add-on:
ddev get mandrasch/ddev-deployer-dep
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:
host('production')
->set('remote_user', 'ploi')
->set('hostname', '123.456.789.123')
->set('deploy_path', '~/my-statamic-site.mandrasch.dev');
To connect to the server via SSH, you need to add your local public SSH key to your ploi profile:
On Mac you'll get your public key via
cat ~/.ssh/id_rsa.pub
Paste it in and send it to your servers:
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
Afterwards we can try our first Deployer run locally via DDEV:
ddev dep deploy production
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] git@github.com: Permission denied (publickey).
[production] fatal: Could not read from remote repository.
[production] Please make sure you have the correct access rights
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/id_rsa.pub
again. Add this key to the Deploy Keys of your private repository on github.com:
You should now have a success moment after running ddev dep deploy production
:
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:
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:
Find the "View test" link and open your site in the browser. Visit /cp
and add a home page:
Now you should have a functioning home page:
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:
The current/
-folder:
The shared/
-folder, this will be kept during deployment changes:
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)
https://github.com/deployphp/action
Monitoring
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:
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:
- See How do I connect with SSH? (Mac & Linux) and How do I connect with SSH? (Windows) for more information on SSH
- Good series as well, but things are done there slightly different https://lorisleiva.com/deploy-your-laravel-app-from-scratch
- Statameet 2021 | Zero-Downtime deployment (Jonas Siewertsen), slightly different as this workflow as well
- My notes for DDEV & Statamic: https://my-ddev-lab.mandrasch.eu/tutorials/cms-and-frameworks/statamic.html
Coming soon - next articles:
- Article about DDEV pull via rsync to get the production site data for local development
- Article about BrowserSync support in DDEV (https://github.com/tyler36/ddev-browsersync#laravel-mix-example) for LaravelMix or Vite (https://stackoverflow.com/questions/72130137/ddev-laravel-vite-websockets-with-soketi-trouble-with-port-configuration/72405747)
Top comments (1)
Update: If you run into "Error: key on use" with the second project, this tutorial is very helpful: blog.harveydelaney.com/configuring...