How to Install/Manage Laravel on Ubuntu 22.04 LTS

Check PHP Version (both cases)

Every Laravel distrubution has a compatible PHP version, so you need to check your PHP version before installing Laravel. Run the below command:

root@mail:~# php -v 

You can see all PHP’s your server has by running this command:

root@mail:~# update-alternatives –config php

If Laravel distrubition is 5.x to 8.x, you need to go for PHP7.4. For distributions >= 9.x, you need to go for PHP >=8.0. You can check Laravel documentation by clicking here. You can refer to Install PHP on Ubuntu article.

Install The Required PHP Extensions (both cases)

After installing the compatible PHP version, we need to install MySQL extension by running the command:

root@mail:~# apt install php7.4-mysql

Also, if your website deals with images, we need to install the PHP GD extension by running the command:

root@mail:~# apt install php7.4-gd

Laravel will need some PHP extensions like below:

openssl
PDO
mbstring
tokenizer
xml
ctype
json

So check your server by running the command:

root@mail:~# php -m

If they’re not installed, you can install them by running:

root@mail:~# apt install openssl PDO mbstring tokenizer xml ctype json

Once you finish the installation, remove the repository we added to install PHP and its extensions by running the command below. Notice that after removing it, when you run apt-upgrade, it will not offer you to upgrade to a higher PHP veresion. So this way you can upgrade the PHP manually whenever you want.

 root@mail:~# add-apt-repository ppa:ondrej/php --remove

Case 1: Start a New Laravel Project

Install Laravel Composer

Enter tmp directory first:

root@mail:~# cd /tmp

Run the below command:

root@mail:/tmp# curl -sS https://getcomposer.org/installer | php

Move the composer to bin directory

root@mail:/tmp# mv composer.phar /usr/local/bin/composer

Launch Your Project

Enter the html directory by running the command:

root@mail:~# cd /var/www/html

If you want to install the latest version of Laravel, run the below command:

root@mail:/var/www/html# composer create-project --prefer-dist laravel/laravel my-laravel-project

If you want to install a previous distribution, let’s say 5.5.*, run this command:

root@mail:/var/www/html# composer create-project laravel/laravel="5.5.*" my-laravel-project

You will get the below prompt, type “y”, then hit enter key.

Continue as root/super user [yes]?

Once the installation is done, you will get something like this:

83 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
> @php artisan vendor:publish --tag=laravel-assets --ansi --force

   INFO  No publishable resources for tag [laravel-assets].

No security vulnerability advisories found.
> @php artisan key:generate --ansi

   INFO  Application key set successfully.

To see Laravel distrubution in the installed project, just open the composer file inside the project by running the command:

root@mail:~# nano /var/www/html/my-laravel-project/composer.json

You will see it like this:

{
    "name": "laravel/laravel",
    "description": "The Laravel Framework.",
    "keywords": ["framework", "laravel"],
    "license": "MIT",
    "type": "project",
    "require": {
        "php": ">=7.0.0",
        "fideloper/proxy": "~3.3",
        "laravel/framework": "5.5.*",
        "laravel/tinker": "~1.0"
    },
    .
    .
    .

If you install the latest version, you will get something like this:

{
    "name": "laravel/laravel",
    "type": "project",
    "description": "The skeleton application for the Laravel framework.",
    "keywords": ["laravel", "framework"],
    "license": "MIT",
    "require": {
        "php": "^8.1",
        "guzzlehttp/guzzle": "^7.2",
        "laravel/framework": "^10.10",
        "laravel/sanctum": "^3.3",
        "laravel/tinker": "^2.8"
    },
    .
    .
    .

Alternatively, you can enter the project directory and run the below command to find the exact distribution:

root@mail:/var/www/html/vpsprof# php artisan --version

This is the response:

Laravel Framework 10.38.1

Manage Database Files (Database Migration)

Laravel offers creating a database structure for all database tables in PHP files. So these PHP files that have the database structure of the tables called database migrations. To find the database migrations, go to this directory:

root@mail:~# ls /var/www/html/my-laravel-project/database/migrations/

The result is like this for a new project created in 2023:

2014_10_12_000000_create_users_table.php                  2019_08_19_000000_create_failed_jobs_table.php
2014_10_12_100000_create_password_reset_tokens_table.php  2019_12_14_000001_create_personal_access_tokens_table.php

So anytime you want to create tables in the database, you just need a simple command.

But before that, we need to create a database and user for our new project. Follow this guide here:  Create New MySQL Database.

After that enter the database info in the .env hidden file. Open the file:

root@mail:/var/www/html/my-laravel-project# nano .env

Enter the value of your database variables as seen below.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=my_laravel_project_db
DB_USERNAME=my_laravel_project_user
DB_PASSWORD=12345678

Note that MySQL doesn’t accept dashes in database name but it’s allowed in the database usernam.

Now, it’s the time to deploy the database tables. Run this comand:

root@mail:/var/www/html/my-laravel-project# php artisan migrate

You will see a result like below:

root@mail:/var/www/html/my-laravel-project# php artisan migrate

   INFO  Preparing database.

  Creating migration table ..................................................................................... 11ms DONE

   INFO  Running migrations.

  2014_10_12_000000_create_users_table ......................................................................... 10ms DONE
  2014_10_12_100000_create_password_reset_tokens_table .......................................................... 3ms DONE
  2019_08_19_000000_create_failed_jobs_table .................................................................... 8ms DONE
  2019_12_14_000001_create_personal_access_tokens_table ........................................................ 11ms DONE

Create Your Own Database File (Database Migration)

We will make a database migration for posts table. The database migration name (php file) is: migrate_posts and the database table name is posts. Run this command inside the project directory:

root@mail:/var/www/html/my-laravel-project# php artisan make:migration migrate_posts --create=posts

The result will be like below:

root@mail:/var/www/html/my-laravel-project# php artisan make:migration migrate_posts --create=posts

   INFO  Migration [database/migrations/2023_12_21_130810_migrate_posts.php] created successfully.

Let’s open the database migration file by running the command:

root@mail:/var/www/html/my-laravel-project# nano database/migrations/2023_12_21_130810_migrate_posts.php

Here’s the default content:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->timestamps();

$table->increments('id');
            $table->text('body');
            $table->string('tag_ids');
            $table->string('country');
            $table->string('city');
            $table->string('ip');
            $table->integer('user_id')->unsigned();
            $table->foreign('user_id')->references('id')->on('users');
            $table->timestamps();

        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};

Here’s the file after editing. I commented two lines.

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            //$table->id();
            //$table->timestamps();

            $table->increments('id');
            $table->text('body');
            $table->string('tag_ids');
            $table->string('country');
            $table->string('city');
            $table->string('ip');
            $table->integer('user_id')->unsigned();
            $table->foreign('user_id')->references('id')->on('users');
            $table->timestamps();

        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};

This file has connection with the pre-existing users table. So let’s edit the table users and open the file as below:

root@mail:/var/www/html/my-laravel-project# nano database/migrations/2014_10_12_000000_create_users_table.php

And here’s the updated content:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('users', function (Blueprint $table) {
            //$table->id();
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();

        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('users');
    }
};

Now, let’s publish/create the tables (or call it migrate the database) by running the command:

root@mail:/var/www/html/my-laravel-project# php artisan migrate

It’s successful. We will get like this:

root@mail:/var/www/html/my-laravel-project# php artisan migrate

   INFO  Running migrations.

  2014_10_12_000000_create_users_table ......................................................................... 16ms DONE
  2014_10_12_100000_create_password_reset_tokens_table .......................................................... 3ms DONE
  2019_08_19_000000_create_failed_jobs_table .................................................................... 8ms DONE
  2019_12_14_000001_create_personal_access_tokens_table ........................................................ 12ms DONE
  2023_12_21_130810_migrate_posts ............................................................................... 9ms DONE

MySQL database will have the new tables as below:

MariaDB [my_laravel_project_db]> show tables;
+---------------------------------+
| Tables_in_my_laravel_project_db |
+---------------------------------+
| failed_jobs                     |
| migrations                      |
| password_reset_tokens           |
| personal_access_tokens          |
| posts                           |
| users                           |
+---------------------------------+
6 rows in set (0.002 sec)

 When you start your new project, if there are existing tables and you want to drop them and avoid the error (tables already exist), run the below command:

root@mail:/var/www/html/my-laravel-project# php artisan migrate:fresh

To rollback the migration, run this command. It will drop all the migrated tables in the database.

root@mail:/var/www/html/my-laravel-project# php artisan migrate:rollback

You will see such results:

root@mail:/var/www/html/my-laravel-project# php artisan migrate:rollback

   INFO  Rolling back migrations.

  2023_12_21_130810_migrate_posts .............................................................................. 11ms DONE
  2019_12_14_000001_create_personal_access_tokens_table ......................................................... 3ms DONE
  2019_08_19_000000_create_failed_jobs_table .................................................................... 2ms DONE
  2014_10_12_100000_create_password_reset_tokens_table .......................................................... 3ms DONE
  2014_10_12_000000_create_users_table .......................................................................... 3ms DONE

MySQL database will look like this:

MariaDB [my_laravel_project_db]> show tables;
+---------------------------------+
| Tables_in_my_laravel_project_db |
+---------------------------------+
| migrations                      |
+---------------------------------+
1 row in set (0.002 sec)

You can rollback one step by running this command:

root@mail:/var/www/html/my-laravel-project# php artisan migrate:rollback --step=1

If you run this command after migrating all tables, itwill rollback the last table as below:

root@mail:/var/www/html/my-laravel-project# php artisan migrate:rollback --step=1

   INFO  Rolling back migrations.

  2023_12_21_130810_migrate_posts ............................................................................... 8ms DONE

Case 2: Run a Pre-existing Laravel Project

If you will migrate a Laravel project from one server to another, you don’t need to install the composer. Just do it as if it’s a normal website.

First, you need to make sure that the PHP version on your server is compatible with Laravel distribution. You can check that by going to the file:

root@mail:~# nano /var/www/html/my-laravel-project/composer.json

For example, if the version is 5.x, the compatible PHP version is 7.4. if iRedMail doesn’t use the version 7.4, you need to install PHP7.4. You can refer to Install PHP on Ubuntu article.

Import the Database

You need to create a database and user for the website. Refer to Create New MySQL Database article.

Make sure to import the database of the website you want to migrate by running this command.

root@mail:~# mysql -u my_laravel_project_user -p my_laravel_project_db < /var/www/html/my-laravel-project/my_laravel_project_db.sql

And you need to have the site files too.

Manage Permissions (both cases)

Run the below commands:

root@mail:~# chown -R www-data:www-data /var/www/html/my-laravel-project
root@mail:~# chmod -R 755 /var/www/html/my-laravel-project
root@mail:~# find /var/www/html/my-laravel-project-type f -exec chmod 664 {} \;

We already explained this part in this article: Download WordPress and Get it Ready for Installation

Nginx Virtual Hosts (for both cases)

First, we need to generate the certificate, as below:

root@mail:~# certbot --nginx certonly -d vpsprof.com

You can refer to the article to learn more: Nginx Virtual Hosts Configuration.

Open a virtual hosts file as below:

root@mail:~# nano /etc/nginx/sites-available/vpsprof.com.conf

If iRedMail Doesn’t Use the Compatible PHP version

If iRedMail doesn’t use the same PHP version which is compatible with Laravel distribution, enter this code:

# HTTP
server {

    server_name vpsprof.com www.vpsprof.com;

    # Redirect http://www.site to https://site
    if ($host = www.vpsprof.com) {
        return 301 https://$host$request_uri;
    }

    # Redirect http://site to https://site
    if ($host = vpsprof.com) {
        return 301 https://$host$request_uri;
    }

    # Redirect all insecure http:// requests to https://
    return 301 https://$host$request_uri;
}

# HTTPS
server {

    # ipv4 with http2
    
    ## For Nginx version < 1.25.1
    listen 443 ssl http2;

    ## For Nginx version >= 1.25.1
    #listen 443 ssl;
    #http2 on; 

    # ipv6 with http2
    #listen [::]:443 ssl http2;

    # SSL Certificate
    ssl_certificate /etc/letsencrypt/live/vpsprof.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/vpsprof.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    # Website URL and Directory
    server_name vpsprof.com www.vpsprof.com;
    root /var/www/html/my-laravel-project/public;
    index index.php index.html;

    # Connect Nginx to PHP
    location ~ \.php$ {
        fastcgi_pass    unix:/run/php/php7.4-fpm.sock;
        fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include         fastcgi_params;
    }

    # Don't allow pages to be rendered in an iframe on external domains.
    add_header X-Frame-Options "SAMEORIGIN";

    # MIME sniffing prevention
    add_header X-Content-Type-Options "nosniff";
    # Enable cross-site scripting filter in supported browsers.
    add_header X-Xss-Protection "1; mode=block";

    # Prevent access to hidden files
    location ~* /\.(?!well-known\/) {
        deny all;
    }

    # Prevent access to certain file extensions
    location ~\.(ini|log|conf)$ {
        deny all;
    }

    # Enable WordPress Permanent Links
    # If we don’t add the below, website directories will get 404 error (like: site/admin or  site/sample-page)
    location / {
        try_files $uri $uri/ /index.php?$args;
    }
}

Note that the root directory for Laravel, should end up with public directory like this: /var/www/html/vpsprof/public

If iRedMail Uses the Compatible PHP version

If iRedMail is using the compatible PHP version, you need to enter this code:

# HTTP
server {

    server_name vpsprof.com www.vpsprof.com;

    # Redirect http://www.site to https://site
    if ($host = www.vpsprof.com) {
        return 301 https://$host$request_uri;
    }

    # Redirect http://site to https://site
    if ($host = vpsprof.com) {
        return 301 https://$host$request_uri;
    }

    # Redirect all insecure http:// requests to https://
    return 301 https://$host$request_uri;
}

# HTTPS
server {

    # ipv4 with http2
    
    ## For Nginx version < 1.25.1
    listen 443 ssl http2;

    ## For Nginx version >= 1.25.1
    #listen 443 ssl;
    #http2 on;

    # ipv6 with http2
    #listen [::]:443 ssl http2;

    # SSL Certificate
    ssl_certificate /etc/letsencrypt/live/vpsprof.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/vpsprof.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    # Website URL and Directory
    server_name vpsprof.com www.vpsprof.com;
    root /var/www/html/my-laravel-project/public;
    index index.php index.html;

    ## Below are iRedMail included
    #include /etc/nginx/templates/misc.tmpl;
    #include /etc/nginx/templates/ssl.tmpl;
    #include /etc/nginx/templates/iredadmin.tmpl;
    #include /etc/nginx/templates/roundcube.tmpl;
    #include /etc/nginx/templates/sogo.tmpl;
    #include /etc/nginx/templates/netdata.tmpl;

    # Connect Nginx to PHP
    include /etc/nginx/templates/php-catchall.tmpl;

    #include /etc/nginx/templates/stub_status.tmpl;

    # Don't allow pages to be rendered in an iframe on external domains.
    add_header X-Frame-Options "SAMEORIGIN";

    # MIME sniffing prevention
    add_header X-Content-Type-Options "nosniff";

    # Enable cross-site scripting filter in supported browsers.
    add_header X-Xss-Protection "1; mode=block";

    # Prevent access to hidden files
    location ~* /\.(?!well-known\/) {
        deny all;
    }

    # Prevent access to certain file extensions
    location ~\.(ini|log|conf)$ {
        deny all;
    }

    # Enable WordPress Permanent Links
    # If we don’t add the below, website directories will get 404 error (like: site/admin or  site/sample-page)
    location / {
        try_files $uri $uri/ /index.php?$args;
    }
}

Make symbolic link between the virtual host in sites-available directory and sites-enabled directory by running the command:

root@mail:~# ln -s /etc/nginx/sites-available/vpsprof.com.conf /etc/nginx/sites-enabled

Let’s make sure that Nginx has no problems by running the below command:

root@mail:~# nginx -t

4. In case no errors appear, restart Nginx by running the command for the changes to take effect.

root@mail:~# service nginx restart

Now, you can check your website by clicking here: https://vpsprof.com