Install the Laravel PHP Framework on CentOS 7

Table of Contents

Introduction

Laravel is a popular PHP framework. Perhaps you've developed a PHP web application using this framework and would like to host it on a CentOS server. This tutorial should help you get Laravel up and running.

Setup

Since this tutorial is focused on CentOS 7, you should have SSH access to a CentOS 7 server. I'm working from a fresh install created from the ProfitBricks public image labeled: CentOS-7-server-2017-10-01.

The most recent release of Laravel is 5.5, and it is a LTS (Long Term Support) release. The systems requirements for Laravel are rather modest:

  • PHP >= 7.0.0
  • OpenSSL PHP Extension
  • PDO PHP Extension
  • Mbstring PHP Extension
  • Tokenizer PHP Extension
  • XML PHP Extension

The base repo configured in CentOS 7 would install a PHP 5 version, which will NOT satisfy the requirements listed above. We can confirm this by running yum info php.x86_64 and reviewing this section of the output.

Name        : php
Arch        : x86_64
Version     : 5.4.16
Release     : 42.el7
Size        : 1.4 M
Repo        : base/7/x86_64

The example commands are pre-fixed with sudo, but you can omit that part if you are running as root.

Install PHP 7.1 and Nginx

Since we need PHP 7, lets 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 completed successfully, please run:

sudo yum check-update

and then

sudo yum search php71

should return a good sized list of php71w- packages including:

mod_php71w.x86_64 : PHP module for the Apache HTTP Server
php71w-fpm.x86_64 : PHP FastCGI Process Manager

We'll use nginx as the web server and use FPM to handle PHP processing for this tutorial. Since we already have the Webtatic repo configured, we may as well get the updated version of nginx it contains. We can install the necessary components for Laravel by running:

sudo yum install nginx1w php71w-fpm php71w-pdo php71w-mbstring php71w-xml php71w-common php71w-cli

If you are curious, the openssl and tokenizer PHP extensions are contained in php71w-common, so that is why they are not explicitly listed. Having php71w-cli will let us work with PHP from the command-line which makes getting some things setup a bit easier.

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.

nginx default webpage

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

If we create a test PHP file in /usr/share/nginx/html/test.php and put these contents in it:

<html>
  <head><title>Test Vars</title></head>
  <body>
    <pre>
      <?php
        var_export($_SERVER)
      ?>
    </pre>
  </body>
</html>

We should be able to see the output rendered in our browser by visiting http://YOUR_SERVER_IP_ADDRESS/test.php. You should see the contents of the $_SERVER array with output lines including these:

'SCRIPT_FILENAME' => '/usr/share/nginx/html/test.php',
'REDIRECT_STATUS' => '200',
'SERVER_NAME' => 'localhost',
'SERVER_PORT' => '80',

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
    ______
   / ____/___  ____ ___  ____  ____  ________  _____
  / /   / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
 / /___/ /_/ / / / / / / /_/ / /_/ (__  )  __/ /
 \____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
                     /_/
Composer version 1.5.2 2017-09-11 16:59:25

Usage:
  command [options] [arguments]
...

That is truncated output, you should see a full list of the available options and arguments.

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 testapp directory using composer, run:

sudo composer create-project laravel/laravel testapp

This tells Composer that we want to install the current version of "laravel/laravel" into the "testapp" directory.

Installing laravel/laravel (v5.5.0)
  - Installing laravel/laravel (v5.5.0): Downloading (100%)
Created project in testapp
> @php -r "file_exists('.env') || copy('.env.example', '.env');"
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 68 installs, 0 updates, 0 removals
  - Installing vlucas/phpdotenv (v2.4.0): Loading from cache
  - Installing symfony/css-selector (v3.3.10): Loading from cache
...
<removed a bunch of output>
...
Writing lock file
Generating autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover
Discovered Package: fideloper/proxy
Discovered Package: laravel/tinker
Package manifest generated successfully.
> @php artisan key:generate
Application key [base64:<removed>] set successfully.

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/testapp/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
Max kernel policy version:      28

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.

Laravel Unexpected Value Exception

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/testapp/storage/*
sudo chown -R apache:root /usr/share/nginx/html/testapp/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/testapp/bootstrap/cache(/.*)?'
sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/html/testapp/storage(/.*)?'

Then to apply those changes run:

sudo restorecon -Rv '/usr/share/nginx/html/testapp'

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

Verify

Finally we can refresh the page in the browser:

Laravel Welcome

Success! We are looking at the default "Welcome" screen for Laravel.

Conclusion

We should be all setup to proceed with using the Laravel framework to develop or serve a PHP web application. There are a number of good resources for learning Laravel available, including Laracasts - Lavavel from Scratch.

Please feel free to comment below or open up a new discussion in the Community section of this site. I'd be interested in knowing if you run into any issues following this tutorial or have some helpful suggestions to improve the install and setup process for Laravel on CentOS 7.

  • Good tutorial but I had a few problems with this. I created a droplet in Vultr.com with Centos7, and followed these instructions to the letter, since I dont have much experience with this. I tried it on another vendor's VPS with the default Centos7 image and same problem. I had the following issues: 1) Composer install went ok up to the line "composer install laravel/laravel testapp", which didn’t work, instead I had to use "composer create-project laravel/laravel testapp" . 2) The lines "chown -R apache:root storage/ " and "chown -R apache:root bootstrap/cache" gave errors like "chown: cannot access ‘storage/’: No such file or directory", looks like we were supposed to advance into the testapp folder for this to work 3) The 'semanage' lines gave errors like "-bash: semanage: command not found" until I installed this "yum install policycoreutils-python" 4) I tried the semanage lines again and this time got different errors: "ValueError: SELinux policy is not managed or store cannot be accessed.". I dont know what this means, both lines gave the same error. But I ignored them and pressed on and I seemed to get the right result in the browser. I hope they were not important....

    Thanks for the tutorial, its the only one I have been 'successful' with so far; I'd like to know what target you installed on such that you avoided getting all these errors though.

  • Also after completing installation and rebooting the VPS, the Laravel welcome page at myipaddress/index.php no longer came up, I just got "This site can’t be reached. [myipaddress] refused to connect."

  • Hello Augustus - thank you very much for the helpful feedback. I've made some adjustments to the tutorial to address some of the items you pointed out.

    For issue #1, I still need to double-check the issue you reported with the composer syntax. When putting this together, I did run into a few different issues with Composer but did get it working eventually.

    For issue #2, I updated the command in the tutorial to include the full path.

    For issue #3, I added text regarding policycoreutils-python. This package is included by default with the public CentOS 7 image that ProfitBricks supplies.

    For issue #4, I'm not sure what happened here, but it seems like the system(s) you were working on may not have had SELinux installed or enabled. You can verify by running sudo sestatus. I added some additional text about how to do that to the tutorial.

    Regarding your second comment, this was an oversight on my part. I had forgotten to provide the systemctl enable commands so that the services would start if the server was rebooted. As now noted in the tutorial, you can run sudo systemctl enable nginx and sudo systemctl enable php-fpm and that should take care of that issue.

  • Thanks a lot for the replies, I (we all) appreciate the support!

    With regard to issue #4, SELinux reported as being Disabled, with sestatus. I entered "rpm -qa | grep selinux" and got the following:

    libselinux-2.5-6.el7.x86_64

    libselinux-utils-2.5-6.el7.x86_64

    libselinux-python-2.5-6.el7.x86_64

    So then according to this page "https://www.digitalocean.com/community/tutorials/an-introduction-to-selinux-on-centos-7-part-1-basic-concepts" I installed a few more things:

    yum install policycoreutils policycoreutils-python selinux-policy selinux-policy-targeted libselinux-utils setroubleshoot-server setools setools-console mcstrans

    setenforce 1

    sudo systemctl restart nginx

    And now after a full reboot sestatus shows Enforcing and "rpm -qa | grep selinux" gives:

    libselinux-2.5-11.el7.x86_64

    selinux-policy-3.13.1-166.el7_4.5.noarch

    selinux-policy-targeted-3.13.1-166.el7_4.5.noarch

    libselinux-python-2.5-11.el7.x86_64

    libselinux-utils-2.5-11.el7.x86_64

    Now the semanage lines are accepted without issues. Thanks very much again!

  • Nice work - it sounds like you were able to SELinux running on the server you were testing with.

    I had a chance to verify the composer syntax, and have updated the tutorial to have the correct syntax of:

    sudo composer create-project laravel/laravel testapp
    

    It is possible to use composer require laravel/laravel and composer install - however that won't leave you with the same setup the rest of the tutorial is expecting.

Log In, Add a Comment