This guide explains how to deploy a Brezel instance to a (virtual) server.
For this guide, we assume the base domain is called example.io and Brezel will be accessible under brezel.example.io.
The example IP of the server is given as 5.35.243.342.
:::caution[Make sure to replace the path to your instance and domain in all examples below]
Otherwise, your brezel will not work correctly!
:::
First, create a new key pair on your local machine:
Terminal window
ssh-keygen-C"kibro"
Make sure to put those keys (public and private) in a safe and secure place.
Possibly share them with the team using a password manager (e.g. Passbolt).
Now, still on your local machine run this with the correct values to copy the public key to the server so you can log in with the private key:
Now disable password SSH based authentication in /etc/ssh/sshd_config:
Do the next steps as the kibro user on your server!
:::caution[This will lock out anyone not using a certificate based SSH access]
That includes you if you did not test the certificate based access before!
It will also lock out any other user that does not have a certificate based access set up.
You should only do this if you are sure that noone else needs to access the server via SSH and a password.
:::
Terminal window
PasswordAuthenticationno
Make sure no file in /etc/ssh/sshd_config.d/ overrides this setting.
e.g. on Hetzner servers, you need to delete the file /etc/ssh/sshd_config.d/50-cloud-init.conf
Terminal window
sudosystemctlrestartsshd
Note, on some Ubuntu images the service is called ssh not sshd.
Use sudo systemctl restart ssh if it complains that the service sshd is not found, try this.
Set up a ufw Firewall:
This will block unwanted traffic to your server.
We allow SSH, HTTP and HTTPS traffic here. HTTP is only needed for the certificate auto (re)-generation.
Terminal window
sudoufwallowssh
sudoufwallowhttp
sudoufwallowhttps
sudoufwdisable
sudoufwenable
The disable / enable dance at the end is needed to restart the firewall with the new rules.
Install and setup CrowdSec or fail2ban:
Tools like this will block spammy requests to your server and protect it from brute force attacks.
We will use CrowdSec here, but fail2ban is a good alternative.
You fall into this category if you already have a user with sudo access on the server.
:::tip[For improved security, we recommend always following the step “using certificate based SSH access” from the “hardening” chapter above!]
This makes interaction with the server way more secure and is an easy prevention against simple SSH brute force attacks.
Although, if you cannot disable password based SSH access, this step mostly brings convenience and not security.
:::
Now create a new user for your brezel instance:
Terminal window
sudoadduserbrezel
Add your brezel user to the www-data group to ensure the correct permissions in combination with the Nginx web server we’ll set up and use later on.
Terminal window
sudousermod-aGwww-databrezel
This will be the user that runs and operates the Brezel instance.
Most of the actually Brezel related setup in this guide will use this user, only some commands will require sudo and thus the other user.
Rule of thumb: If sudo is NOT used in a command, try it first with brezel, especially for any php or supervisor commands.
(If additional instructions don’t say otherwise.)
This assumes that the ondrej/php ppa is available. It should be if you followed the steps above.
ImageMagick is needed to generate thumbnails.
Due to weird issues with weird aspect ratio PDFs when generating high DPI (“original”) quality thumbnails, it is
highly recommended to use ImageMagick 7.x!
Unfortunately, the default repositories and the default php8.4-imagick package use ImageMagick 6.x, so we need to build it from source.
It is important that we have the development libraries for various formats (especially the ghostscript libs for PDF support) installed before building ImageMagick from source.
Make sure you uninstall any existing imagick version first to avoid conflicts.
Just run the following commands to be sure (some might error out, but that is fine):
If you indeed got some errors, or just want to verify that it worked, run these commands:
Terminal window
php-m|grepimagick# Should output "imagick"
php--riimagick# Should show version info and you should find "PDF" in the supported formats
Setup for SSL certificates:
There are two variants to choose from. The one we’ve always used is “Certbot”, see option 1.
However, especially when you are dealing with an installation in a closed network that is only accessible via a VPN, you might want to use the “acme.sh” variant instead.
If sudo snap install --classic certbot does not work because you are behind some kind of firewall or don’t have access to snap, you can also install certbot via sudo apt install certbot python3-certbot-nginx
In the installation process, read closely what it asks you and answer accordingly.
You want to remove the test database and disallow remote root login.
And you should set a secure password for the root user.
At this point you can already create a “brezel” database user with the necessary permissions while you are at it.
mariadb -u root -p
CREATE USER 'brezel'@'localhost' IDENTIFIED BY '<your secure password>';
GRANT ALL ON *.* TO 'brezel'@'localhost' WITH GRANT OPTION;
FLUSH PRIVILEGES;
:::caution[Make sure to use a password that fits into the limits of MariaDB]
By default, recent MariaDB with mysql_native_password plugin only allows passwords up to 80 characters.
Especially passphrases generated by password managers can be longer than that.
This also applies to your root user of the db, not just the brezel user.
:::
Configure your php-fpm settings in /etc/php/8.4/fpm/pool.d/www.conf:
pm = dynamic
pm.max_children = 300 ; Handle at max X requests at once
pm.max_requests = 1500 ; Restart worker after X requests to prevent memory leaks
pm.start_servers = 10
pm.min_spare_servers = 10
pm.max_spare_servers = 50
pm.process_idle_timeout = 15s
You can either just paste these values at the end of the file or search for the values and change them.
Now ensure that file permissions are properly kept.
For that edit the php-fpm systemd service file using sudo systemctl edit php8.4-fpm.service
and add the following lines IN BETWEEN THE COMMENTS, as instructed:
This will create SSH keys for the brezel user and add them to GitLab as “deploy keys”.
That way, the server can pull your Brezel from the remote repository.
Switch to the brezel user.
Terminal window
su-brezel
Generate a new key pair:
Terminal window
ssh-keygen-b4096
You will be asked for a path. The default is probably fine.
Just ensure it is in the home directory of the brezel user (~/.ssh/id_<something>).
Hit enter.
You will then be asked for a passphrase.
We want no passphrase.
Leave the field empty and hit enter.
Hit enter again to confirm the empty password.
It will then output something like this:
Now, go to GitLab, to the repository of your Brezel instance and go to Settings > Repository > Deploy Keys.
We want our server to be able to read from the GitLab repository.
Paste the contents of ~/.ssh/id_<something>.pub to the Key field. Give it a meaningful title like Production [brezel.example.io].
Make sure that the instance folder is owned by brezel and www-data with both having permissions to read, write and create files.
We also need to make sure that this ownership is propagated to all subdirectories and files, including newly created files.
To do that, run the following commands:
:::caution[Make sure to replace the path to your instance]
Otherwise, your brezel will not work correctly!
:::
:::caution[Make sure umask is properly set]
Ensure that php-fpm has UMask=0002 set in its systemd service file as described above!
Your async queues running via supervisor also need to have this configured as above!
Otherwise, newly created files and folders via SPA will not have the correct permissions and your brezel will break in mysterious ways.
E.g. async jobs not having access to some files or folders.
:::
Switch back to kibro and run:
Terminal window
# Set your project's root directory
APP_PATH="/var/www/vhosts/api.brezel.example.io"
# Set ownership to your user and the www-data group
sudochown-Rbrezel:www-data ${APP_PATH}
# Set permissions for all directories to 2775 (rwxrwx-r-x) including the setgid bit
sudofind ${APP_PATH} -typed-execchmod2775{}\;
# Set permissions for all files to 664 (rw-rw-r--)
sudofind ${APP_PATH} -typef-execchmod664{}\;
# Apply the 'setgid' bit to all directories, just in case
sudofind ${APP_PATH} -typed-execchmodg+s{}\;
# Ensure laravel specific directories are writable as per docs (technically already done, but better be safe than sorry)
sudochmod-R2775 ${APP_PATH}/storage
# Make sure acl is installed
sudoapt-getinstallacl
# Make sure existing folders that need it follow the rules that both brezel and www-data can read/write them
:::tip[Use brezel/api 3.2.3 or higher to have better handling of folder permissions]
This version introduced a workaround for a stupid laravel behaviour (https://github.com/laravel/framework/issues/30367),
so folders created via SPA will have the correct permissions so that your brezel user can access them from queue-jobs
to read/write files in them and vice versa.
:::
In general the Brezel instance will be deployed to the API subdomain.
The SPA domain will receive the contents of the minified frontend found in the dist/ folder.
:::note
This is the one to use when you followed this guide.
:::
Or, if you have root privileges, you can create a brezel MySQL/MariaDB user with ALL privileges:
GRANT ALL ON *.* TO 'brezel'@'localhost' identified by '<password>' WITH GRANT OPTION;
FLUSH PRIVILEGES;
:::caution[You don’t need to do that if you did it while installing MariaDB above!]
There we already created a brezel user with the necessary permissions.
:::
:::danger[You will need to allow SSH for the brezel user]
This is needed for the pipeline to work.
Change to the brezel user if you are not already and run cat ~/.ssh/id_<something>.pub >> ~/.ssh/authorized_keys.
Use the same value you put into GitLab as the deploy key.
:::
:::tip[Now you can trigger an initial deployment by pushing to the main branch]
This should trigger the pipeline and deploy the instance.
:::
Now all dependencies should be installed, the needed files for SPA and API should be in place and the environment should be set up.
Note that the pipeline should have NOT failed, but your system will not be ready yet!
For that, the next steps are here.
After this block, the system will be up and running ans subsequent pipelines will just update as usual.
:::danger[Make sure to be the brezel user when running ANY bakery command]
Failure to do this NOW OR LATER will break your instance as it will create files with the wrong permissions.
:::
You can now create your system by its name by running the following command:
Terminal window
phpbakerysystemcreatekab
Finally, run the setup commands:
Terminal window
phpbakeryinit--force
phpbakerymigrate--force
phpbakeryapply
phpbakeryload--force
phpbakerymake:supervisor
:::note[These are the same commands the pipeline would run]
You could also just re-trigger the pipeline after creating the system
:::
We now only need to do one more file-permission fix before everything is ready
This will update the permissions of the newly created auth keys to that both www-data and the brezel user can read
and write them which is needed for login to work!
After this, a basic Brezel should be up and running. For the full functionality, you need to set up additional services.
Available since:brezel/api@1.0.0
Since 1.0.0, Brezel ships with a Laravel Reverb based Brotcast server integrated!
Once you setup Supervisor, the server will be started automatically.
If you are not using the Laravel Reverb based Brotcast server, setup brotcast via Docker
:::caution[This feature requires a container runtime like docker]
Make sure you have docker installed.
:::
Then create a docker-compose.yml file with the following content:
If you want to use event/cron, you need to set up a cronjob.
Very basically: this should trigger php bakery schedule in the directory of your instance every minute as the user that normally runs your brezel (when using nginx and following this setup it will be www-data).
How you achieve this depends on you, especially on your local setup, but on a server it makes sense to use cron.
Make sure you are the brezel user and run
Terminal window
crontab-e
Now add the following line:
:::caution[Make sure to replace the path]
Otherwise, crons will not run and just fail silently.
:::
:::caution[This feature requires a container runtime like docker]
Install it if possible!
See the docker docs.
In permission constrained environments udocker may be used as an alternative.
You will however have more friction setting up the service and possibly some compatibility issues.
:::
If your brezel does stuff like creating pdfs or filling .docx templates, you need to set up the export service.
This is a container based environment that provides services like WKHtmlToPdf, Pandoc and more.
If you choose to use the Docker variant, make sure you add the kibro user to the docker group and continue this installation as the kibro user.
Adding brezel to the docker group is not recommended as it gives root-like permissions to the brezel user, which is not desired.
The easiest way is to install bun and then follow the guide in the linked Repo.
Hint: do this in /home/kibro/ in a new directory called export.
:::note[If you use a 1.x or lower version of brezel/api you need to set up the export service manually]
In the installer repository metioned above, there is a old_export-docker_compose.yml file that you can use to set up the export service manually.
Remove the services you don’t need (you should always at least keep OCR).
Then run sudo docker compose up -d in the same directory as the file.
:::
Now you need to configure the export service in your .env file:
Supervisor handles the starting and stopping all the php-based services Brezel needs and ships with brezel/api.
That includes brotcast (if you use Reverb), the queues and so on.
Switch back to the brezel user and go into the instance directory.
:::caution[If you have a brezel/api version higher than 3.0, you need to adjust permissions]
Make sure that this is in your systems .env file or else the supervisor jobs will try to run as www-data which will NOT work:
BREZEL_JOBS_SUPERVISOR_USER=brezel
:::
Now copy supervisord.conf.example to supervisord.conf and adjust the socket names if desired.
Now run the following command to create the supervisor configuration:
Terminal window
phpbakerymake:supervisor
Now we register our supervisor programms with the supervisor daemon
:::caution[Do not interact with supervisorctl directly more that necessary]
Restarting using the supervisorctl restart command or other reloading actions are not recommended as it can mess up permissions if done via the wrong user.
If you want to interact with it, make sure you use the brezel user, but even then, no guarantees.
:::
If you want to restart the supervisor service, do so using the following command:
Terminal window
sudosystemctlrestartsupervisord-brezel
Stopping the service is done with:
Terminal window
sudosystemctlstopsupervisord-brezel
Seeing the status of the services can be done with:
Terminal window
systemctlstatussupervisord-brezel
:::note
This is the only valid usecase to use supervisorctl directly.
Run supervisorctl status in the instance directory to see the status of the services.