#
PHP Application Server
This documentation can be used as a reference to create a basic web application server running Apache and PHP. This includes several optional steps if you would like to include an additional Apache VirtualHost for a staging environment or would like Supervisor to run Laravel artisan
for Queues or WebSockets.
The hostname referenced throughout this documentation will be app.example.org
and staging.app.example.org
, which must be replaced by your actual hostname.
This Enterprise Linux 9 documentation should work with compatible derivatives of Fedora, including RedHat Enterprise, Rocky Linux, and AlmaLinux. If you find any subtle differences, please report the inconsistency or create a pull request with the improvements.
This documentation assumes a few things, including:
- you have done an installation from the Minimal ISO image.
- that your server has internet connectivity and a hostname (e.g.,
app.example.org
). - that you have SSH access to the server.
#
Update operating system and install packages
SSH into the server and
sudo
to root:ssh user@app.example.org sudo -s
Set the hostname of the server using the NetworkManager CLI (
nmcli
) program:nmcli general hostname app.example.org
Enable the CodeReady Builder (CRB) repository frequently used by EPEL:
dnf config-manager --set-enabled crb
Install the Extra Packages for Enterprise Linux (EPEL) and Remi PHP repositories:
dnf -y install \ https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm \ https://rpms.remirepo.net/enterprise/remi-release-9.rpm
Install then run
screen
(a good practice before updating/installing packages over a remote connection):dnf -y install screen && screen
Update the operating system:
dnf -y update
If a kernel update happened, you might as well reboot now:
reboot
Enable the Remi PHP repository and activate the PHP 8.3 module:
dnf config-manager --set-enabled remi dnf module -y enable php:remi-8.3
Install some awesome packages including Apache, OpenSSL, PHP, Git, MariaDB Client, Supervisor, SELinux Tools, and several other useful programs:
dnf -y install curl \ git \ httpd \ mariadb \ mod_ssl \ openssl \ php-bcmath \ php-cli \ php-devel \ php-fpm \ php-gd \ php-imap \ php-intl \ php-json \ php-ldap \ php-mbstring \ php-mysqlnd \ php-opcache \ php-pdo \ php-pecl-redis \ php-pecl-zip \ php-sodium \ php-tidy \ php-xml \ php-xmlrpc \ policycoreutils-python-utils \ supervisor \ unzip \ vim \ wget
If your PHP application will need to make connections to external servers (e.g., APIs, database servers), you will need to set the
httpd_can_network_connect
andhttpd_can_network_connect_db
options so that SELinux will allow Apache to make network and database connections:setsebool -P httpd_can_network_connect 1 setsebool -P httpd_can_network_connect_db 1
Start PHP-FPM, Apache, and Supervisor and set them to start when the system boots:
systemctl enable --now php-fpm systemctl enable --now httpd systemctl enable --now supervisord
Allow HTTP and HTTPS traffic and reload the firewall:
firewall-cmd --add-service=http --add-service=https --permanent firewall-cmd --reload
#
Create a production environment
Create a new service account called
production
, which will be used to deploy your PHP application with a tool like Deployer:useradd -m production
Create and set permissions on the SSH
authorized_keys
file for theproduction
user.mkdir /home/production/.ssh touch /home/production/.ssh/authorized_keys chown -R production:production /home/production/.ssh chmod 700 /home/production/.ssh chmod 600 /home/production/.ssh/authorized_keys
Add your SSH public key (the output of
cat ~/.ssh/id_rsa.pub
on your computer) to the newauthorized_keys
file.vim /home/production/.ssh/authorized_keys
Create and appropriately permission the Apache document root and application storage directories for production.
mkdir -p /var/www/vhosts/app.example.org/storage/logs mkdir /var/www/vhosts/app.example.org/storage/cache chown -R production:production /var/www/vhosts/app.example.org chown production:apache /var/www/vhosts/app.example.org chmod 750 /var/www/vhosts/app.example.org
Configure SELinux so the PHP application can access and write to the storage directory, then apply the SELinux context changes:
semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/vhosts/app.example.org/storage(/.*)?" semanage fcontext -a -t httpd_log_t "/var/www/vhosts/app.example.org/storage/logs(/.*)?" semanage fcontext -a -t httpd_cache_t "/var/www/vhosts/app.example.org/storage/cache(/.*)?" restorecon -R -v /var/www/vhosts/app.example.org
If your PHP application writes data to areas other than the previously defined
storage
directory, you will need to set and apply thehttpd_sys_rw_content_t
context to that location/file as well. For example, if you install Wordpress into the/var/www/vhosts/app.example.org/current/public
directory, you would need to run:semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/vhosts/app.example.org/current/public(/.*)?" restorecon -R -v /var/www/vhosts/app.example.org
Append
127.0.0.1 app.example.org
to the/etc/hosts
file:echo "127.0.0.1 app.example.org" | tee -a /etc/hosts
#
Create a staging environment
Setting up a staging VirtualHost is entirely optional. In most cases it is not advisable to run your staging environment on the same server as you production environment; however, it is technically possible and this documentation can be used as a reference regardless of whether staging is on the same environment as production or a dedicated server.
Create a new service account called
staging
, which will be used to deploy the staging version of your PHP application with a tool like Deployer:useradd -m staging
Create and permission the SSH
authorized_keys
file for thestaging
user.mkdir /home/staging/.ssh touch /home/staging/.ssh/authorized_keys chown -R staging:staging /home/staging/.ssh chmod 700 /home/staging/.ssh chmod 600 /home/staging/.ssh/authorized_keys
Add your SSH public key to the new
authorized_keys
file.vim /home/staging/.ssh/authorized_keys
Create and appropriately permission the Apache document root and application storage directories for staging.
mkdir -p /var/www/vhosts/staging.app.example.org/storage/logs mkdir /var/www/vhosts/staging.app.example.org/storage/cache chown -R staging:staging /var/www/vhosts/staging.app.example.org chown staging:apache /var/www/vhosts/staging.app.example.org chmod 750 /var/www/vhosts/staging.app.example.org
Configure SELinux so the PHP application can access and write to the storage directory, then apply the SELinux context changes:
semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/vhosts/staging.app.example.org/storage(/.*)?" semanage fcontext -a -t httpd_log_t "/var/www/vhosts/staging.app.example.org/storage/logs(/.*)?" semanage fcontext -a -t httpd_cache_t "/var/www/vhosts/staging.app.example.org/storage/cache(/.*)?" restorecon -R -v /var/www/vhosts/staging.app.example.org
Append
127.0.0.1 staging.app.example.org
to the/etc/hosts
file:echo "127.0.0.1 staging.app.example.org" | tee -a /etc/hosts
#
Generate TLS certificates
Generate the private keys required for each of your hostnames:
mkdir -p /root/certs openssl genrsa -out /root/certs/app.example.org.key 4096 openssl genrsa -out /root/certs/staging.app.example.org.key 4096
Generate the TLS Certificate Signing Requests (CSR) that you will need to provide to your Certificate Authority when requesting a TLS (SSL) Certificate.
Do this for
app.example.org
:openssl req -new -key /root/certs/app.example.org.key -out /root/certs/app.example.org.csr
Do this for
staging.app.example.org
:openssl req -new -key /root/certs/staging.app.example.org.key -out /root/certs/staging.app.example.org.csr
You will be asked a number of questions, answer accordingly, but do not answer anything for "Email Address", "A challenge password", or "An optional company name."
You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [XX]:CA State or Province Name (full name) []:Ontario Locality Name (eg, city) [Default City]:Kingston Organization Name (eg, company) [Default Company Ltd]:Silentweb Organizational Unit Name (eg, section) []:Technical Documentation Common Name (eg, your name or your server's hostname) []:app.example.org Email Address []: Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:
Once you've received your new certificate from your Certificate Authority, create a .crt file for each hostname and paste in the contents of the certificate. You will also likely receive a root chain certificate, so paste the contents of that into a new file called
ca-certificate.crt
in the same directory.vim /root/certs/app.example.org.crt vim /root/certs/staging.app.example.org.crt vim /root/certs/ca-certificate.crt
If you are only creating self-signed certificates, you should do this for each hostname:
openssl x509 -req -days 365 -in /root/certs/app.example.org.csr -signkey /root/certs/app.example.org.key -out /root/certs/app.example.org.crt openssl x509 -req -days 365 -in /root/certs/staging.app.example.org.csr -signkey /root/certs/staging.app.example.org.key -out /root/certs/staging.app.example.org.crt
Once finished, you will end up with a
/root/certs
directory that looks as follows:[root@app certs]# ls -1 app.example.org.crt app.example.org.csr app.example.org.key ca-certificate.crt staging.app.example.org.crt staging.app.example.org.csr staging.app.example.org.key
Copy the certificates in the Apache VirtualHost directory:
mkdir /var/www/vhosts/app.example.org/certs/ cp /root/certs/app.example.org.crt /var/www/vhosts/app.example.org/certs/ cp /root/certs/app.example.org.key /var/www/vhosts/app.example.org/certs/ mkdir /var/www/vhosts/staging.app.example.org/certs/ cp /root/certs/staging.app.example.org.crt /var/www/vhosts/staging.app.example.org/certs/ cp /root/certs/staging.app.example.org.key /var/www/vhosts/staging.app.example.org/certs/
#
Configure Apache and PHP
Create a new file called
/etc/php.d/app.ini
and add the following, making sure to adjust to your own supported timezone:date.timezone = America/Toronto display_errors = Off error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT expose_php = Off memory_limit = 512M post_max_size = 512M session.cookie_secure = 1 session.cookie_httponly = 1 session.cookie_samesite = Strict upload_max_filesize = 512M
Create a PHP-FPM Pool for the
production
user by creating a new file called/etc/php-fpm.d/production.conf
and add the following:; Start a new pool named 'production'. [production] ; Unix user/group of processes user = production group = production listen = /run/php-fpm/app.example.org.sock listen.acl_users = apache,nginx listen.allowed_clients = 127.0.0.1 pm = dynamic pm.max_children = 50 pm.start_servers = 5 pm.min_spare_servers = 5 pm.max_spare_servers = 35
Create a PHP-FPM Pool for the
staging
user by creating a new file called/etc/php-fpm.d/staging.conf
and add the following:; Start a new pool named 'staging'. [staging] ; Unix user/group of processes user = staging group = staging listen = /run/php-fpm/staging.app.example.org.sock listen.acl_users = apache,nginx listen.allowed_clients = 127.0.0.1 pm = dynamic pm.max_children = 50 pm.start_servers = 5 pm.min_spare_servers = 5 pm.max_spare_servers = 35
Create the Apache VirtualHost for
app.example.org
by creating a new file called/etc/httpd/conf.d/000-app.example.org.conf
and add the following:# This will limit what information Apache reveals about itself. ServerTokens Prod ServerSignature Off TraceEnable Off # Enable OCSP stapling. SSLStaplingCache "shmcb:logs/stapling-cache(150000)" # Production: app.example.org <VirtualHost *:80> ServerName app.example.org ServerAdmin hostmaster@example.org RewriteEngine On RewriteCond %{HTTPS} off RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} </VirtualHost> <VirtualHost *:443> ServerName app.example.org:443 ServerAdmin hostmaster@example.org SSLEngine on SSLProtocol -all +TLSv1.2 SSLHonorCipherOrder on SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH SSLCertificateFile /var/www/vhosts/app.example.org/certs/app.example.org.crt SSLCertificateKeyFile /var/www/vhosts/app.example.org/certs/app.example.org.key #SSLCACertificateFile /var/www/vhosts/app.example.org/certs/ca-certificate.crt SSLUseStapling on Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains;" Header always set X-Frame-Options SAMEORIGIN DocumentRoot /var/www/vhosts/app.example.org/current/public <Directory "/var/www/vhosts/app.example.org/current/public"> <IfModule mod_proxy_fcgi.c> <Files ~ (\.php$)> SetHandler proxy:unix:/run/php-fpm/app.example.org.sock|fcgi://127.0.0.1:9000 </Files> </IfModule> Options FollowSymLinks Require all granted AllowOverride all </Directory> # Using WebSockets? You can enable this by uncommenting the following lines. #ProxyPass "/app/" "ws://localhost:6000/app/" #ProxyPassReverse "/app/" "ws://localhost:6000/app/" #ProxyPass "/apps/" "http://localhost:6000/apps/" #ProxyPassReverse "/apps/" "http://localhost:6000/apps/" </VirtualHost>
Create the Apache VirtualHost for
staging.app.example.org
by creating a new file called/etc/httpd/conf.d/000-staging.app.example.org.conf
and add the following:# This will limit what information Apache reveals about itself. ServerTokens Prod ServerSignature Off TraceEnable Off # Enable OCSP stapling. SSLStaplingCache "shmcb:logs/stapling-cache(150000)" # Staging: staging.app.example.org <VirtualHost *:80> ServerName staging.app.example.org ServerAdmin hostmaster@example.org RewriteEngine On RewriteCond %{HTTPS} off RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} </VirtualHost> <VirtualHost *:443> ServerName staging.app.example.org:443 ServerAdmin hostmaster@example.org SSLEngine on SSLProtocol -all +TLSv1.2 SSLHonorCipherOrder on SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH SSLCertificateFile /var/www/vhosts/staging.app.example.org/certs/staging.app.example.org.crt SSLCertificateKeyFile /var/www/vhosts/staging.app.example.org/certs/staging.app.example.org.key #SSLCACertificateFile /var/www/vhosts/staging.app.example.org/certs/ca-certificate.crt SSLUseStapling on Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains;" Header always set X-Frame-Options SAMEORIGIN DocumentRoot /var/www/vhosts/staging.app.example.org/current/public <Directory "/var/www/vhosts/staging.app.example.org/current/public"> <IfModule mod_proxy_fcgi.c> <Files ~ (\.php$)> SetHandler proxy:unix:/run/php-fpm/staging.app.example.org.sock|fcgi://127.0.0.1:9000 </Files> </IfModule> Options FollowSymLinks Require all granted AllowOverride all </Directory> # Using WebSockets? You can enable this by uncommenting the following lines. #ProxyPass "/app/" "ws://localhost:6001/app/" #ProxyPassReverse "/app/" "ws://localhost:6001/app/" #ProxyPass "/apps/" "http://localhost:6001/apps/" #ProxyPassReverse "/apps/" "http://localhost:6001/apps/" </VirtualHost>
Test that your Apache configuration is okay, then restart Apache and PHP-FPM:
apachectl configtest systemctl restart httpd systemctl restart php-fpm
#
Configure Supervisor
Supervisor has many uses, but in this example we are using it to run Laravel Queue and Worker. If you don't need something like this running, simply skip this or use it as a reference later on.
Create a new file called
/etc/supervisord.d/app.example.org.ini
and use the following template snippet as a reference to create your own file.Make sure you have the correct path in
command
andstdout_logfile
, and thatuser
matches the correct service account used to deploy your app.[program:app-queue-production] process_name=%(program_name)s_%(process_num)02d command=php /var/www/vhosts/app.example.org/current/artisan queue:work --queue=high,emails,default,low --env=production autostart=true autorestart=true user=production numprocs=1 redirect_stderr=true stdout_logfile=/var/www/vhosts/app.example.org/storage/logs/worker.log [program:app-websocket-production] process_name=%(program_name)s_%(process_num)02d command=php /var/www/vhosts/app.example.org/current/artisan websockets:serve --port=6000 --env=production autostart=true autorestart=true user=production numprocs=1 redirect_stderr=true stdout_logfile=/var/www/vhosts/app.example.org/storage/logs/websocket.log [group:app-server] programs=app-queue-production,app-websocket-production
Create a new file called
/etc/supervisord.d/staging.app.example.org.ini
and use the following template snippet as a reference to create your own file.Make sure you have the correct path in
command
andstdout_logfile
, and thatuser
matches the correct service account used to deploy your app.[program:app-queue-staging] process_name=%(program_name)s_%(process_num)02d command=php /var/www/vhosts/staging.app.example.org/current/artisan queue:work --queue=high,emails,default,low --env=staging autostart=true autorestart=true user=staging numprocs=1 redirect_stderr=true stdout_logfile=/var/www/vhosts/staging.app.example.org/storage/logs/worker.log [program:app-websocket-staging] process_name=%(program_name)s_%(process_num)02d command=php /var/www/vhosts/staging.app.example.org/current/artisan websockets:serve --port=6001 --env=staging autostart=true autorestart=true user=staging numprocs=1 redirect_stderr=true stdout_logfile=/var/www/vhosts/staging.app.example.org/storage/logs/websocket.log [group:app-server] programs=app-queue-staging,app-websocket-staging
Restart Supervisor:
systemctl restart supervisord
This documentation assumes a few things, including:
- you have done a fresh installation from Ubuntu ISO image.
- that your server has internet connectivity and a hostname (e.g.,
app.example.org
). - that you have SSH access to the server.
#
Update operating system and install packages
SSH into the server and
sudo
to root:ssh user@app.example.org sudo -s
Set the hostname of the server using the
hostnamectl
program:hostnamectl set-hostname app.example.org
Run
screen
(a good practice before updating/installing packages over a remote connection):screen
Update the operating system:
apt-get update apt-get upgrade
If a kernel update happened, you might as well reboot now:
reboot
Install some awesome packages including Apache, OpenSSL, PHP, Git, MariaDB Client, Supervisor, and several other useful programs:
apt-get install -y apache2 \ curl \ git \ mariadb-client \ openssl \ php8.3-bcmath \ php8.3-cli \ php8.3-curl \ php8.3-fpm \ php8.3-gd \ php8.3-imap \ php8.3-intl \ php8.3-ldap \ php8.3-mbstring \ php8.3-mysql \ php8.3-opcache \ php8.3-redis \ php8.3-tidy \ php8.3-xml \ php8.3-xmlrpc \ php8.3-zip \ supervisor \ unzip \ wget
Start PHP-FPM, Apache, and Supervisor and set them to start when the system boots:
systemctl enable --now php8.3-fpm systemctl enable --now apache2 systemctl enable --now supervisor
#
Create a production environment
Create a new service account called
production
, which will be used to deploy your PHP application with a tool like Deployer:useradd -m production
Create and set permissions on the SSH
authorized_keys
file for theproduction
user.mkdir /home/production/.ssh touch /home/production/.ssh/authorized_keys chown -R production:production /home/production/.ssh chmod 700 /home/production/.ssh chmod 600 /home/production/.ssh/authorized_keys
Add your SSH public key (the output of
cat ~/.ssh/id_rsa.pub
on your computer) to the newauthorized_keys
file.vim /home/production/.ssh/authorized_keys
Create and appropriately permission the Apache document root and application storage directories for production.
mkdir -p /var/www/vhosts/app.example.org/storage/logs mkdir /var/www/vhosts/app.example.org/storage/cache chown -R production:production /var/www/vhosts/app.example.org chown production:www-data /var/www/vhosts/app.example.org chmod 750 /var/www/vhosts/app.example.org
Append
127.0.0.1 app.example.org
to the/etc/hosts
file:echo "127.0.0.1 app.example.org" | tee -a /etc/hosts
#
Create a staging environment
Setting up a staging VirtualHost is entirely optional. In most cases it is not advisable to run your staging environment on the same server as you production environment; however, it is technically possible and this documentation can be used as a reference regardless of whether staging is on the same environment as production or a dedicated server.
Create a new service account called
staging
, which will be used to deploy the staging version of your PHP application with a tool like Deployer:useradd -m staging
Create and permission the SSH
authorized_keys
file for thestaging
user.mkdir /home/staging/.ssh touch /home/staging/.ssh/authorized_keys chown -R staging:staging /home/staging/.ssh chmod 700 /home/staging/.ssh chmod 600 /home/staging/.ssh/authorized_keys
Add your SSH public key to the new
authorized_keys
file.vim /home/staging/.ssh/authorized_keys
Create and appropriately permission the Apache document root and application storage directories for staging.
mkdir -p /var/www/vhosts/staging.app.example.org/storage/logs mkdir /var/www/vhosts/staging.app.example.org/storage/cache chown -R staging:staging /var/www/vhosts/staging.app.example.org chown staging:www-data /var/www/vhosts/staging.app.example.org chmod 750 /var/www/vhosts/staging.app.example.org
Append
127.0.0.1 staging.app.example.org
to the/etc/hosts
file:echo "127.0.0.1 staging.app.example.org" | tee -a /etc/hosts
#
Generate TLS certificates
Generate the private keys required for each of your hostnames:
mkdir -p /root/certs openssl genrsa -out /root/certs/app.example.org.key 4096 openssl genrsa -out /root/certs/staging.app.example.org.key 4096
Generate the TLS Certificate Signing Requests (CSR) that you will need to provide to your Certificate Authority when requesting a TLS (SSL) Certificate.
Do this for
app.example.org
:openssl req -new -key /root/certs/app.example.org.key -out /root/certs/app.example.org.csr
Do this for
staging.app.example.org
:openssl req -new -key /root/certs/staging.app.example.org.key -out /root/certs/staging.app.example.org.csr
You will be asked a number of questions, answer accordingly, but do not answer enter anything for "Email Address", "A challenge password", or "An optional company name."
You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [XX]:CA State or Province Name (full name) []:Ontario Locality Name (eg, city) [Default City]:Kingston Organization Name (eg, company) [Default Company Ltd]:Silentweb Organizational Unit Name (eg, section) []:Technical Documentation Common Name (eg, your name or your server's hostname) []:app.example.org Email Address []: Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:
Once you've received your new certificate from your Certificate Authority, create a .crt file for each hostname and paste in the contents of the certificate. You will also likely receive a root chain certificate, so paste the contents of that into a new file called
ca-certificate.crt
in the same directory.vim /root/certs/app.example.org.crt vim /root/certs/staging.app.example.org.crt vim /root/certs/ca-certificate.crt
If you are only creating self-signed certificates, you should do this for each hostname:
openssl x509 -req -days 365 -in /root/certs/app.example.org.csr -signkey /root/certs/app.example.org.key -out /root/certs/app.example.org.crt openssl x509 -req -days 365 -in /root/certs/staging.app.example.org.csr -signkey /root/certs/staging.app.example.org.key -out /root/certs/staging.app.example.org.crt
Once finished, you will end up with a
/root/certs
directory that looks as follows:[root@app certs]# ls -1 app.example.org.crt app.example.org.csr app.example.org.key ca-certificate.crt staging.app.example.org.crt staging.app.example.org.csr staging.app.example.org.key
Copy the certificates in the Apache VirtualHost directory:
mkdir /var/www/vhosts/app.example.org/certs/ cp /root/certs/app.example.org.crt /var/www/vhosts/app.example.org/certs/ cp /root/certs/app.example.org.key /var/www/vhosts/app.example.org/certs/ mkdir /var/www/vhosts/staging.app.example.org/certs/ cp /root/certs/staging.app.example.org.crt /var/www/vhosts/staging.app.example.org/certs/ cp /root/certs/staging.app.example.org.key /var/www/vhosts/staging.app.example.org/certs/
#
Configure Apache and PHP
Enable several useful Apache modules:
a2enmod ssl rewrite headers proxy proxy_http proxy_balancer expires
Create a new file called
/etc/php/8.3/mods-available/app.ini
and add the following, making sure to adjust to your own supported timezone:date.timezone = America/Toronto display_errors = Off error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT expose_php = Off memory_limit = 512M post_max_size = 512M session.cookie_secure = 1 session.cookie_httponly = 1 session.cookie_samesite = Strict upload_max_filesize = 512M
Enable these new PHP settings by typing:
phpenmod app
Create a PHP-FPM Pool for the
production
user by creating a new file called/etc/php/8.3/fpm/pool.d/production.conf
and add the following:; Start a new pool named 'production'. [production] ; Unix user/group of processes user = production group = production listen = /run/php/app.example.org.sock listen.allowed_clients = 127.0.0.1 pm = dynamic pm.max_children = 50 pm.start_servers = 5 pm.min_spare_servers = 5 pm.max_spare_servers = 35
Create a PHP-FPM Pool for the
staging
user by creating a new file called/etc/php/8.3/fpm/pool.d/staging.conf
and add the following:; Start a new pool named 'staging'. [staging] ; Unix user/group of processes user = staging group = staging listen = /run/php/staging.app.example.org.sock listen.allowed_clients = 127.0.0.1 pm = dynamic pm.max_children = 50 pm.start_servers = 5 pm.min_spare_servers = 5 pm.max_spare_servers = 35
Create the Apache VirtualHost for
app.example.org
by creating a new file called/etc/apache2/sites-available/000-app.example.org.conf
and add the following:# This will limit what information Apache reveals about itself. ServerTokens Prod ServerSignature Off TraceEnable Off # Enable OCSP stapling. SSLStaplingCache "shmcb:logs/stapling-cache(150000)" # Production: app.example.org <VirtualHost *:80> ServerName app.example.org ServerAdmin hostmaster@example.org RewriteEngine On RewriteCond %{HTTPS} off RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} </VirtualHost> <VirtualHost *:443> ServerName app.example.org:443 ServerAdmin hostmaster@example.org SSLEngine on SSLProtocol -all +TLSv1.2 SSLHonorCipherOrder on SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH SSLCertificateFile /var/www/vhosts/app.example.org/certs/app.example.org.crt SSLCertificateKeyFile /var/www/vhosts/app.example.org/certs/app.example.org.key #SSLCACertificateFile /var/www/vhosts/app.example.org/certs/ca-certificate.crt SSLUseStapling on Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains;" Header always set X-Frame-Options SAMEORIGIN DocumentRoot /var/www/vhosts/app.example.org/current/public <Directory "/var/www/vhosts/app.example.org/current/public"> <IfModule mod_proxy_fcgi.c> <Files ~ (\.php$)> SetHandler proxy:unix:/run/php/app.example.org.sock|fcgi://127.0.0.1:9000 </Files> </IfModule> Options FollowSymLinks Require all granted AllowOverride all </Directory> # Using WebSockets? You can enable this by uncommenting the following lines. #ProxyPass "/app/" "ws://localhost:6000/app/" #ProxyPassReverse "/app/" "ws://localhost:6000/app/" #ProxyPass "/apps/" "http://localhost:6000/apps/" #ProxyPassReverse "/apps/" "http://localhost:6000/apps/" </VirtualHost>
Create the Apache VirtualHost for
staging.app.example.org
by creating a new file called/etc/apache2/sites-available/000-staging.app.example.org.conf
and add the following:# This will limit what information Apache reveals about itself. ServerTokens Prod ServerSignature Off TraceEnable Off # Enable OCSP stapling. SSLStaplingCache "shmcb:logs/stapling-cache(150000)" # Staging: staging.app.example.org <VirtualHost *:80> ServerName staging.app.example.org ServerAdmin hostmaster@example.org RewriteEngine On RewriteCond %{HTTPS} off RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} </VirtualHost> <VirtualHost *:443> ServerName staging.app.example.org:443 ServerAdmin hostmaster@example.org SSLEngine on SSLProtocol -all +TLSv1.2 SSLHonorCipherOrder on SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH SSLCertificateFile /var/www/vhosts/staging.app.example.org/certs/staging.app.example.org.crt SSLCertificateKeyFile /var/www/vhosts/staging.app.example.org/certs/staging.app.example.org.key #SSLCACertificateFile /var/www/vhosts/staging.app.example.org/certs/ca-certificate.crt SSLUseStapling on Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains;" Header always set X-Frame-Options SAMEORIGIN DocumentRoot /var/www/vhosts/staging.app.example.org/current/public <Directory "/var/www/vhosts/staging.app.example.org/current/public"> <IfModule mod_proxy_fcgi.c> <Files ~ (\.php$)> SetHandler proxy:unix:/run/php/staging.app.example.org.sock|fcgi://127.0.0.1:9000 </Files> </IfModule> Options FollowSymLinks Require all granted AllowOverride all </Directory> # Using WebSockets? You can enable this by uncommenting the following lines. #ProxyPass "/app/" "ws://localhost:6001/app/" #ProxyPassReverse "/app/" "ws://localhost:6001/app/" #ProxyPass "/apps/" "http://localhost:6001/apps/" #ProxyPassReverse "/apps/" "http://localhost:6001/apps/" </VirtualHost>
Enable
app.example.org
andstaging.app.example.org
:a2ensite 000-app.example.org a2ensite 000-staging.app.example.org
Test that your Apache configuration is okay, then restart Apache and PHP-FPM:
apachectl configtest systemctl restart apache2 systemctl restart php8.3-fpm
#
Configure Supervisor
Supervisor has many uses, but in this example we are using it to run Laravel Queue and Worker. If you don't need something like this running, simply skip this or use it as a reference later on.
Create a new file called
/etc/supervisor/conf.d/app.example.org.ini
and use the following template snippet as a reference to create your own file.Make sure you have the correct path in
command
andstdout_logfile
, and thatuser
matches the correct service account used to deploy your app.[program:app-queue-production] process_name=%(program_name)s_%(process_num)02d command=php /var/www/vhosts/app.example.org/current/artisan queue:work --queue=high,emails,default,low --env=production autostart=true autorestart=true user=production numprocs=1 redirect_stderr=true stdout_logfile=/var/www/vhosts/app.example.org/storage/logs/worker.log [program:app-websocket-production] process_name=%(program_name)s_%(process_num)02d command=php /var/www/vhosts/app.example.org/current/artisan websockets:serve --port=6000 --env=production autostart=true autorestart=true user=production numprocs=1 redirect_stderr=true stdout_logfile=/var/www/vhosts/app.example.org/storage/logs/websocket.log [group:app-server] programs=app-queue-production,app-websocket-production
Create a new file called
/etc/supervisor/conf.d/staging.app.example.org.ini
and use the following template snippet as a reference to create your own file.Make sure you have the correct path in
command
andstdout_logfile
, and thatuser
matches the correct service account used to deploy your app.[program:app-queue-staging] process_name=%(program_name)s_%(process_num)02d command=php /var/www/vhosts/staging.app.example.org/current/artisan queue:work --queue=high,emails,default,low --env=staging autostart=true autorestart=true user=staging numprocs=1 redirect_stderr=true stdout_logfile=/var/www/vhosts/staging.app.example.org/storage/logs/worker.log [program:app-websocket-staging] process_name=%(program_name)s_%(process_num)02d command=php /var/www/vhosts/staging.app.example.org/current/artisan websockets:serve --port=6001 --env=staging autostart=true autorestart=true user=staging numprocs=1 redirect_stderr=true stdout_logfile=/var/www/vhosts/staging.app.example.org/storage/logs/websocket.log [group:app-server] programs=app-queue-staging,app-websocket-staging
Restart Supervisor:
systemctl restart supervisord