How to install Laravel on Digital Ocean Cloud Instance
This blog post is a helper document for novice Laravel developers trying to set up a Laravel app on a Digital Ocean Cloud Instance. We have considered a basic shared CPU with CentOS operating system.
The systems requirements for Laravel are rather modest:
- PHP >= 7.3
- BCMath PHP Extension
- Ctype PHP Extension
- Fileinfo PHP Extension
- JSON PHP Extension
- Mbstring PHP Extension
- OpenSSL PHP Extension
- PDO PHP Extension
- Tokenizer PHP Extension
- XML PHP Extension
The example commands are prefixed with sudo, but you can omit that part if you are running as root.
Step 1: Add PHP 7.3 Remi repository
Since we need PHP 7, let us add access to both EPEL and the Webtatic repository to gain access to the packages available there.
sudo rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
sudo rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm&
Once those commands have been completed successfully, please run:
sudo yum check-update
and then
sudo yum search php73
should return a good-sized list of php73w- packages including:
mod_php73w.x86_64 : PHP module for the Apache HTTP Server
php73w-fpm.x86_64 : PHP FastCGI Process Manager
Step 2: Install PHP 7.3 on CentOS 8
sudo yum install nginx1w php73w-fpm php73w-pdo php73w-mbstring php73w-xml php73w-common php73w-cli
Check version installed
$ php -v/code>
Step 3: Installing other PHP 7.3 Extensions
Install PHP 7.3 extensions by using the syntax:
sudo yum install php73w-< entension-name >
As an example, to install a mysql module for PHP applications that use MySQL databases, you’ll run:
sudo yum install php73w-mysql
Configure Nginx and PHP-FPM
To get nginx running we need to open up access to port 80 through the firewall:
sudo firewall-cmd --add-port=80/tcp
sudo firewall-cmd --permanent --add-port=80/tcp
Now start nginx with the default config by running:
sudo systemctl start nginx
If you want nginx to be started when the server boots, then also run:
sudo systemctl enable nginx
Now if you visit the IP address of your server you should see the default nginx page.
With basic static content being served properly, lets start php-fpm.
sudo systemctl start php-fpm
Once again, if you want php-fpm to be started when the server boots, then also run:
sudo systemctl enable php-fpm
By default, that will start listening for connections on localhost port 9000. We can follow the nginx PHP FastCGI example to allow nginx to pass requests for PHP pages to php-fpm.
For now we'll place the contents of the location directive shown on that page into the /etc/nginx/nginx.conf file near the bottom of the default server block that starts on line 49. (You can use whatever editor you like. For vi the full command would be sudo vi /etc/nginx/nginx.conf)
location ~ [^/]\.php(/|$) { fastcgi_split_path_info ^(.+?\.php)(/.*)$; if (!-f $document_root$fastcgi_script_name) { return 404;}
# Mitigate https://httpoxy.org/ vulnerabilities
fastcgi_param HTTP_PROXY "";
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
# include the fastcgi_param setting
include fastcgi_params;
# SCRIPT_FILENAME parameter is used for PHP FPM determining
# the script name. If it is not set in fastcgi_params file,
# i.e. /etc/nginx/fastcgi_params or in the parent contexts,
# please comment off following line:
# fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;}
Note: Make sure and uncomment the last commented line of what we are pasting in.
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
If we do not have that uncommented, then we will not see any PHP output in our browser when we run a test in a minute. Now restart nginx and php-fpm with the following commands:
sudo systemctl restart nginx
sudo systemctl restart php-fpm
Install Composer and Laravel
We should now be ready to install composer and use it to install Laravel. I'm running these commands from inside my home directory. ( cd ~ ) Run this to automatically download a script that will be used to install composer.
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
The suggested method to verify the installation script hasn't been tampered with.
php -r "if (hash_file('SHA384', 'composer-setup.php') === '544e09ee996cdf60ece3804abc52599c22b1f40f4323403c44d44fdfdd586475ca9813a858088ffbc1f233e9b180f061') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
That should respond with "Installer verified". If it does, then run:
php composer-setup.php
Which will respond with:
All settings correct for using Composer
Downloading...
Composer (version 1.5.2) successfully installed to: /usr/share/nginx/html/composer.phar
Use it: php composer.phar
Remove the install script with:
rm composer-setup.php
Which may prompt you for confirmation:
rm: remove regular file composer-setup.php? y
Now we can run:
php composer.phar
For convenience, you can copy composer.phar into a directory that is included in the $PATH environment variable. For example:
sudo cp composer.phar /usr/bin/composer
Or you could create a symbolic link to it using: sudo ln -s /home/YOUR_USERNAME/composer /usr/bin/composer.phar
then you can execute it by simply running composer.
Lets go ahead and move to the nginx document root in /usr/share/nginx/html:
cd /usr/share/nginx/html
To install the Laravel framework into a new example-app directory using composer, run:
sudo composer create-project laravel/laravel example-app
Once that has completed, we need to adjust our nginx document root and then test.
Edit /etc/nginx/nginx.conf and around line 52 in the default server block you should see the line:
root /usr/share/nginx/html;
We need to change it to:
root /usr/share/nginx/html/example-app/public;
Now we can check our syntax and then restart nginx:
sudo nginx -t
sudo systemctl restart nginx Resolve Ownership and SELinux Errors
SELinux is enabled by default when using the ProfitBricks CentOS 7 image. You can determine if this is the case with your server by running:
sudo sestatus
It will return some results similar this:
SELinux status: enabled
SELinuxfs mount: /sys/fs/selinux
SELinux root directory: /etc/selinux
Loaded policy name: targeted
Current mode: enforcing
Mode from config file: enforcing
Policy MLS status: enabled
Policy deny_unknown status: allowed
Memory protection checking: actual (secure)
Max kernel policy version: 33
If it shows SELinux status: enabled and Current mode: enforcing then this section will likely apply to you and you'll need to follow the next set of steps. If SELinux has been disabled or the mode is permissive then you can skip this. Please try to access http://YOUR_SERVER_IP_ADDRESS/index.php in a browser now. If SELinux is enabled you will likely see an UnexpectedValueException error screen.
This error can be caused by the directory and file ownership along with the security context being enforced by SELinux on a couple of directories that Laravel needs to write to. We can work around this by running:
sudo chown -R apache:root /usr/share/nginx/html/example-app/storage/*
sudo chown -R apache:root /usr/share/nginx/html/example-app/bootstrap/cache
This changes the ownership of those directories to apache which is the user that php-fpm is running as by default.
To resolve the SELinux issue, we need to update the security context from httpd_sys_content_t to httpd_sys_rw_content_t. This can be done by running semanage which is part of the policycoreutils-python package. (You can install it with sudo yum install policycoreutils-python if necessary, but it should already be installed when using the ProfitBricks CentOS 7 image.)
sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/html/example-app/bootstrap/cache(/.*)?'
sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/html/example-app/storage(/.*)?'
Then to apply those changes run:
sudo restorecon -Rv '/usr/share/nginx/html/example-app'
and if you want to verify the change, run ls -Z which should generate output similar to this:
drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 app
-rwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 artisan
drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 bootstrap
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 composer.json
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 composer.lock
drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 config
drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 database
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 package.json
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 phpunit.xml
drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 public
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 readme.md
drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 resources
drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 routes
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 server.php
drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_rw_content_t:s0 storage
drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 tests
drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 vendor
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 webpack.mix.js
You'll notice that storage shows httpd_sys_rw_content_t instead of httpd_sys_content_t. The same should be true of the directories under bootstrap