#
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
sudoto root:ssh user@app.example.org sudo -sSet the hostname of the server using the NetworkManager CLI (
nmcli) program:nmcli general hostname app.example.orgEnable the CodeReady Builder (CRB) repository frequently used by EPEL:
dnf config-manager --set-enabled crbInstall 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.rpmInstall then run
screen(a good practice before updating/installing packages over a remote connection):dnf -y install screen && screenUpdate the operating system:
dnf -y updateIf a kernel update happened, you might as well reboot now:
rebootEnable the Remi PHP repository and activate the PHP 8.3 module:
dnf config-manager --set-enabled remi dnf module -y enable php:remi-8.3Install 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 \ wgetIf 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_connectandhttpd_can_network_connect_dboptions 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 1Start 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 supervisordAllow 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 productionCreate and set permissions on the SSH
authorized_keysfile for theproductionuser.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_keysAdd your SSH public key (the output of
cat ~/.ssh/id_rsa.pubon your computer) to the newauthorized_keysfile.vim /home/production/.ssh/authorized_keysCreate 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.orgConfigure 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.orgIf your PHP application writes data to areas other than the previously defined
storagedirectory, you will need to set and apply thehttpd_sys_rw_content_tcontext to that location/file as well. For example, if you install Wordpress into the/var/www/vhosts/app.example.org/current/publicdirectory, 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.orgAppend
127.0.0.1 app.example.orgto the/etc/hostsfile: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 stagingCreate and permission the SSH
authorized_keysfile for thestaginguser.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_keysAdd your SSH public key to the new
authorized_keysfile.vim /home/staging/.ssh/authorized_keysCreate 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.orgConfigure 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.orgAppend
127.0.0.1 staging.app.example.orgto the/etc/hostsfile: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 4096Generate 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.csrDo this for
staging.app.example.org:openssl req -new -key /root/certs/staging.app.example.org.key -out /root/certs/staging.app.example.org.csrYou 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.crtin the same directory.vim /root/certs/app.example.org.crt vim /root/certs/staging.app.example.org.crt vim /root/certs/ca-certificate.crtIf 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.crtOnce finished, you will end up with a
/root/certsdirectory 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.keyCopy 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.iniand 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 = 512MCreate a PHP-FPM Pool for the
productionuser by creating a new file called/etc/php-fpm.d/production.confand 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 = 35Create a PHP-FPM Pool for the
staginguser by creating a new file called/etc/php-fpm.d/staging.confand 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 = 35Create the Apache VirtualHost for
app.example.orgby creating a new file called/etc/httpd/conf.d/000-app.example.org.confand 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.orgby creating a new file called/etc/httpd/conf.d/000-staging.app.example.org.confand 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.iniand use the following template snippet as a reference to create your own file.Make sure you have the correct path in
commandandstdout_logfile, and thatusermatches 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-productionCreate a new file called
/etc/supervisord.d/staging.app.example.org.iniand use the following template snippet as a reference to create your own file.Make sure you have the correct path in
commandandstdout_logfile, and thatusermatches 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-stagingRestart 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
sudoto root:ssh user@app.example.org sudo -sSet the hostname of the server using the
hostnamectlprogram:hostnamectl set-hostname app.example.orgRun
screen(a good practice before updating/installing packages over a remote connection):screenUpdate the operating system:
apt-get update apt-get upgradeIf a kernel update happened, you might as well reboot now:
rebootInstall 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 \ wgetStart 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 productionCreate and set permissions on the SSH
authorized_keysfile for theproductionuser.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_keysAdd your SSH public key (the output of
cat ~/.ssh/id_rsa.pubon your computer) to the newauthorized_keysfile.vim /home/production/.ssh/authorized_keysCreate 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.orgAppend
127.0.0.1 app.example.orgto the/etc/hostsfile: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 stagingCreate and permission the SSH
authorized_keysfile for thestaginguser.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_keysAdd your SSH public key to the new
authorized_keysfile.vim /home/staging/.ssh/authorized_keysCreate 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.orgAppend
127.0.0.1 staging.app.example.orgto the/etc/hostsfile: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 4096Generate 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.csrDo this for
staging.app.example.org:openssl req -new -key /root/certs/staging.app.example.org.key -out /root/certs/staging.app.example.org.csrYou 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.crtin the same directory.vim /root/certs/app.example.org.crt vim /root/certs/staging.app.example.org.crt vim /root/certs/ca-certificate.crtIf 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.crtOnce finished, you will end up with a
/root/certsdirectory 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.keyCopy 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 expiresCreate a new file called
/etc/php/8.3/mods-available/app.iniand 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 = 512MEnable these new PHP settings by typing:
phpenmod appCreate a PHP-FPM Pool for the
productionuser by creating a new file called/etc/php/8.3/fpm/pool.d/production.confand 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 = 35Create a PHP-FPM Pool for the
staginguser by creating a new file called/etc/php/8.3/fpm/pool.d/staging.confand 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 = 35Create the Apache VirtualHost for
app.example.orgby creating a new file called/etc/apache2/sites-available/000-app.example.org.confand 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.orgby creating a new file called/etc/apache2/sites-available/000-staging.app.example.org.confand 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.organdstaging.app.example.org:a2ensite 000-app.example.org a2ensite 000-staging.app.example.orgTest 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.iniand use the following template snippet as a reference to create your own file.Make sure you have the correct path in
commandandstdout_logfile, and thatusermatches 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-productionCreate a new file called
/etc/supervisor/conf.d/staging.app.example.org.iniand use the following template snippet as a reference to create your own file.Make sure you have the correct path in
commandandstdout_logfile, and thatusermatches 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-stagingRestart Supervisor:
systemctl restart supervisord