Skip to content

Instantly share code, notes, and snippets.

@vicgonvt
Last active March 17, 2024 06:51
Show Gist options
  • Save vicgonvt/cd0431a5cdc043ebab7f4954f7b4d471 to your computer and use it in GitHub Desktop.
Save vicgonvt/cd0431a5cdc043ebab7f4954f7b4d471 to your computer and use it in GitHub Desktop.
Deployment Guide for Ubuntu Server from Scratch with Laravel
title keywords description date tags permalink img author authorlink
Setting Up Laravel in Ubuntu / DigitalOcean
servers, laravel, coderstape, coder's tape
Let's take a look at settting up a server from scratch for Laravel.
April 1, 2019
servers, laravel
setting-up-laravel-in-ubuntu-digitalocean
Victor Gonzalez

In this post, we are looking at the steps necessary to create an Ubuntu droplet in DigitalOcean from scratch. This is the companion guide to the video series in Laravel 5.8 from scrath. Follow along with those to get the video guide.

Part 1 https://coderstape.com/lesson/112-deployment-basic-server-setup-part-1

Part 2 https://coderstape.com/lesson/113-deployment-basic-server-setup-part-2

Part 3 https://coderstape.com/lesson/114-deployment-basic-server-setup-part-3

Getting Started

  • Create droplet with Ubuntu 18.10
  • ssh root@[DROPLET IP ADDRESS]
  • Get password from your email
  • Change password on first login
  • adduser laravel
  • Enter password and other information
  • usermod -aG sudo laravel

Locking Down to SSH Key only (Extremely Important)

  • In your local machine, ssh-keygen
  • Generate a key, if you leave passphrase blank, no need for password
  • ls ~/.ssh to show files in local machine
  • Get the public key, cat ~/.ssh/id_rsa.pub
  • Copy it
  • cd ~/.ssh and vim authorized_keys
  • Paste key
  • Repeat steps for laravel user
  • su laravel then mkdir ~/.ssh fix permissions chmod 700 ~/.ssh
  • vim ~/.ssh/authorized_keys and paste key
  • chmod 600 ~/.ssh/authorized_keys to restrict this from being modified
  • exit to return to root user

Disable Password from Server

  • sudo vim /etc/ssh/sshd_config
  • Find PasswordAuthentication and set that to no
  • Turn on PubkeyAuthentication yes
  • Turn off ChallengeResponseAuthentication no
  • Reload the SSH service sudo systemctl reload sshd
  • Test new user in a new tab to prevent getting locked out

Setting Up Firewall

  • View all available firewall settings
  • sudo ufw app list
  • Allow on OpenSSH so we don't get locked out
  • sudo ufw allow OpenSSH
  • Enable Firewall
  • sudo ufw enable
  • Check the status
  • sudo ufw status

Install Linux, Nginx, MySQL, PHP

Nginx

  • sudo apt update enter root password
  • sudo apt install nginx enter Y to install
  • sudo ufw app list For firewall
  • sudo ufw allow 'Nginx HTTP' to add NGINX
  • sudo ufw status to verify change
  • Visit server in browser

MySQL

  • sudo apt install mysql-server enter Y to install
  • sudo mysql_secure_installation to run automated securing script
  • Press N for VALIDATE PASSWORD plugin
  • Set root password
  • Remove anonymous users? Y
  • Disallow root login remotely? N
  • Remove test database and access to it? Y
  • Reload privilege tables now? Y
  • sudo mysql to enter MySQL CLI
  • SELECT user,authentication_string,plugin,host FROM mysql.user; to verify root user's auth method
  • ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'STRONG_PASSWORD_HERE'; to set a root password
  • SELECT user,authentication_string,plugin,host FROM mysql.user; to verify root user's auth method
  • FLUSH PRIVILEGES; to apply all changes
  • mysql -u root -p to access db from now on, enter password STRONG_PASSWORD_HERE

PHP & Basic Nginx

  • sudo add-apt-repository universe to add software repo
  • sudo apt install php-fpm php-mysql to install the basic PHP software
  • sudo vim /etc/nginx/sites-available/YOUR.DOMAIN.COM
server {
        listen 80;
        root /var/www/html;
        index index.php index.html index.htm index.nginx-debian.html;
        server_name YOUR.DOMAIN.COM;

        location / {
                try_files $uri $uri/ =404;
        }

        location ~ \.php$ {
                include snippets/fastcgi-php.conf;
                fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
        }

        location ~ /\.ht {
                deny all;
        }
}
  • sudo ln -s /etc/nginx/sites-available/YOUR.DOMAIN.COM /etc/nginx/sites-enabled/ to create symlink to enabled sites
  • sudo unlink /etc/nginx/sites-enabled/default to remove default link
  • sudo nginx -t test the whole config
  • sudo systemctl reload nginx to apply all changes
  • sudo vim /var/www/html/info.php to start a new PHP file, fill it with <?php phpinfo();
  • sudo rm /var/www/html/info.php optional command to get rid of test file

Let's Dial in The Laravel Ecosystem

  • sudo apt-get install php7.2-mbstring php7.2-xml composer unzip
  • mysql -u root -p Login to create the Laravel DB
  • CREATE DATABASE laravel DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
  • GRANT ALL ON laravel.* TO 'laraveluser'@'localhost' IDENTIFIED BY 'password';
  • FLUSH PRIVILEGES;
  • exit
  • cd /var/www/html, sudo mkdir -p first-project
  • sudo chown laravel:laravel first-project
  • git clone https://github.com/coderstape/laravel-58-from-scratch.git .
  • composer install
  • cp .env.example .env, and then vim .env
APP_NAME=Laravel
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=http://YOUR.DOMAIN.COM

LOG_CHANNEL=stack

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=root
DB_USERNAME=laravel
DB_PASSWORD=STRONG_PASSWORD_HERE
  • php artisan migrate
  • php artisan key:generate to generate the key
  • sudo chgrp -R www-data storage bootstrap/cache fix permissions
  • sudo chmod -R ug+rwx storage bootstrap/cache fix permissions
  • sudo chmod -R 755 /var/www/html/first-project fix permissions
  • chmod -R o+w /var/www/html/first-project/storage/ fix permission

Modify Nginx

  • sudo vim /etc/nginx/sites-available/YOUR.DOMAIN.COM
server {
    listen 80;
    listen [::]:80;

    root /var/www/html/first-project/public;
    index index.php index.html index.htm index.nginx-debian.html;

    server_name YOUR.DOMAIN.COM;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
    }

    location ~ /\.ht {
            deny all;
    }
}
  • sudo nginx -t
  • sudo systemctl reload nginx reload Nginx

Let's Encrypt

  • sudo add-apt-repository ppa:certbot/certbot to get repo
  • sudo apt install python-certbot-nginx to install
  • sudo certbot certonly --webroot --webroot-path=/var/www/html/quickstart/public -d example.com -d www.example.com
  • sudo certbot certonly --webroot --webroot-path=/var/www/html/first-project/public -d YOUR.DOMAIN.COM

Final mod for Nginx

  • sudo vim /etc/nginx/sites-available/YOUR.DOMAIN.COM
server {
    listen 80;
    listen [::]:80;

    server_name YOUR.DOMAIN.COM;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name YOUR.DOMAIN.COM;
    root /var/www/html/first-project/public;

    ssl_certificate /etc/letsencrypt/live/YOUR.DOMAIN.COM/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/YOUR.DOMAIN.COM/privkey.pem;
    
    ssl_protocols TLSv1.2;
	ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
	ssl_prefer_server_ciphers on;

	add_header X-Frame-Options "SAMEORIGIN";
	add_header X-XSS-Protection "1; mode=block";
	add_header X-Content-Type-Options "nosniff";

	index index.php index.html index.htm index.nginx-debian.html;

    charset utf-8;

    location / {
            try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
    }

    location ~ /\.ht {
            deny all;
    }

    location ~ /.well-known {
            allow all;
    }
}
  • sudo nginx -t
  • sudo ufw app list For firewall
  • sudo ufw allow 'Nginx HTTPS' to add NGINX
  • sudo ufw status to verify change
  • sudo systemctl reload nginx reload Nginx

Extra Credit

Let's make the prompt pretty

  • sudo apt-get install zsh to install ZSH
  • zsh --version to confirm install
  • whereis zsh to find out where it is
  • sudo usermod -s /usr/bin/zsh $(whoami) to make Zsh default
  • sudo reboot to reapply all changes
  • 2 to populate a default file
  • sudo apt-get install powerline fonts-powerline to install powerline
  • sudo apt-get install zsh-theme-powerlevel9k to install Theme
  • echo "source /usr/share/powerlevel9k/powerlevel9k.zsh-theme" >> ~/.zshrc to enable the theme in your Zshrc
  • exit and login again to see the new theme
  • sh -c "$(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)" for Oh My Zsh
  • echo "source /usr/share/powerlevel9k/powerlevel9k.zsh-theme" >> ~/.zshrc to re-enable 9K
@stesvis
Copy link

stesvis commented Jan 3, 2020

Hello, I followed all the steps but I am getting this error:

 sudo apt-get install php7.2-mbstring php7.2-xml composer unzip
Reading package lists... Done
Building dependency tree
Reading state information... Done
E: Unable to locate package php7.2-mbstring
E: Couldn't find any package by glob 'php7.2-mbstring'
E: Couldn't find any package by regex 'php7.2-mbstring'
E: Unable to locate package php7.2-xml
E: Couldn't find any package by glob 'php7.2-xml'
E: Couldn't find any package by regex 'php7.2-xml'

Any idea? thanks for the tutorial!

@gabrielsmenezes
Copy link

Hello, I followed all the steps but I am getting this error:

 sudo apt-get install php7.2-mbstring php7.2-xml composer unzip
Reading package lists... Done
Building dependency tree
Reading state information... Done
E: Unable to locate package php7.2-mbstring
E: Couldn't find any package by glob 'php7.2-mbstring'
E: Couldn't find any package by regex 'php7.2-mbstring'
E: Unable to locate package php7.2-xml
E: Couldn't find any package by glob 'php7.2-xml'
E: Couldn't find any package by regex 'php7.2-xml'

Any idea? thanks for the tutorial!

I think you need to install php7.2-mbstring and php7.2-xml. You can see if you have mbstring enable with:

php -i | grep mbstring

If you got:

Multibyte decoding support using mbstring => disabled

Try:

install apt-get install php7.2-mbstring

After installed run again:

php -i | grep mbstring

and you ll get something like:

/etc/php/7.2/cli/conf.d/20-mbstring.ini, Zend Multibyte Support => provided by mbstring Multibyte decoding support using mbstring => enabled mbstring mbstring extension makes use of "streamable kanji code filter and converter", which is distributed under the GNU Lesser General Public License version 2.1. mbstring.detect_order => no value => no value mbstring.encoding_translation => Off => Off mbstring.func_overload => 0 => 0 mbstring.http_input => no value => no value mbstring.http_output => no value => no value mbstring.http_output_conv_mimetypes => ^(text/|application/xhtml\+xml) => ^(text/|application/xhtml\+xml) mbstring.internal_encoding => no value => no value mbstring.language => neutral => neutral mbstring.strict_detection => Off => Off mbstring.substitute_character => no value => no value

@faizeee
Copy link

faizeee commented Jan 5, 2020

Thank you

@stesvis
Copy link

stesvis commented Jan 6, 2020

@gabrielsmenezes I restarted with a new instance of Ubuntu 18.0.4 instead of Ubuntu 19 and it works fine now, thanks!

@gabrielsmenezes
Copy link

@stesvis nice :)

@akseries
Copy link

Thank you for sharing this stuff. It is absolutely great tutorial.

@yavgel85
Copy link

Great tutorial! Helped a lot.

@yavgel85
Copy link

Please сan you create record a tutorial about How to Set Up a Full-Featured Mail Server on Ubuntu 18.04 (e.g. with PostFix / iRedMail)

@jaouharach
Copy link

A very clear and detailed guide. Thank you for sharing !

@ahmedseaf
Copy link

thank you very mush

@jewishmoses
Copy link

jewishmoses commented Apr 7, 2020

chmod -R 777

I wouldn't suggest such thing.
“chmod 777” means making the file readable, writable and executable by everyone.

@csiki96
Copy link

csiki96 commented Apr 22, 2020

Encryption not working anymore therefore I dont get the ssl certificate, anybody have an idea how to fix it?

@Cod3rMax
Copy link

How to know what is the nameserver so i can put my domain name on the droplet?

@Malware02
Copy link

Thank you so much! <3

@csiki96
Copy link

csiki96 commented May 20, 2020

Encryption not working anymore therefore I dont get the ssl certificate, anybody have an idea how to fix it?

If u have problems, just restart nginx

@dgloriaweb
Copy link

Hi, please change this to sudo:
chmod -R o+w /var/www/html/first-project/storage/ fix permission

@youssefkhouili
Copy link

Thank you so much

@BobbyBriQz
Copy link

Thanks for this ✌🏽

@hendisantika
Copy link

Thank you for sharing this stuff. It is absolutely great tutorial.
It helps me a lot.

@hendisantika
Copy link

Sometimes when I run: GRANT ALL ON laravel.* TO 'laraveluser'@'localhost' IDENTIFIED BY 'password';

I have error like this:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'IDENTIFIED BY 'muhamm4dy'' at line 1

Why? How we fix this?

Thanks

@csiki96
Copy link

csiki96 commented Nov 17, 2020

Sometimes when I run: GRANT ALL ON laravel.* TO 'laraveluser'@'localhost' IDENTIFIED BY 'password';

I have error like this:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'IDENTIFIED BY 'muhamm4dy'' at line 1

Why? How we fix this?

Thanks

remove second quotation mark
from this: 'muhamm4dy' '
to this: 'muhamm4dy'

@hendisantika
Copy link

hendisantika commented Nov 17, 2020

No, it's from MySQL console. I already put Password like that.
GRANT ALL ON laravel.* TO 'laraveluser'@'localhost' IDENTIFIED BY 'password';

@hendisantika
Copy link

Everytime run this command error happens:

MariaDB [(none)]> SELECT user,authentication_string,plugin,host FROM mysql.user;
+--------------+-----------------------+-----------------------+--------------------------+
| User         | authentication_string | plugin                | Host                     |
+--------------+-----------------------+-----------------------+--------------------------+
| mariadb.sys  |                       | mysql_native_password | localhost                |
| root         | invalid               | mysql_native_password | localhost                |
| hendisantika | invalid               | mysql_native_password | localhost                |
|              |                       |                       | localhost                |
|              |                       |                       | hendis-macbook-pro.local |
+--------------+-----------------------+-----------------------+--------------------------+
5 rows in set (0.004 sec)

MariaDB [(none)]> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root';
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'BY 'root'' at line 1

Any advice?
Thanks

@mostafizurhimself
Copy link

Not working on Laravel 8, Only home page is showing. Any other routes are not accessible. It returns 404 error.

@nasrulfuad
Copy link

No, it's from MySQL console. I already put Password like that.
GRANT ALL ON laravel.* TO 'laraveluser'@'localhost' IDENTIFIED BY 'password';

I had the same error, i've try with GRANT ALL PRIVILEGES ON database_name.* TO 'username'@'localhost'; and it works.

@Jagdish-J-P
Copy link

Not working on Laravel 8, Only home page is showing. Any other routes are not accessible. It returns 404 error.

edit this file

sudo vim /etc/nginx/sites-available/YOUR.DOMAIN.COM

Try with replacing

location / {
          try_files $uri $uri/ =404;
 }

with

location / {
       try_files $uri $uri/ /index.php$is_args$args;
 }

I know it's late but may be helpful for someone else.

@abedcodes
Copy link

this gist is truly a good refrence :) thanks dear sir victor

in the Let's Dial in The Laravel Ecosystem section, fourth line which is about granting all privileges to root user on newly created database, the command should be
GRANT ALL PRIVILEGES ON laravel.* TO 'root'@'localhost';
thanks to this article link

@abedcodes
Copy link

about these four lines of permission settings,

image
there is a conflict between command 2 & 3, at line 2 we give the user & group full access to the storage & bootstrap/cache(ie xwr),
in the next line however we change the permissions on all files of project including those two directories to 755 which means taking away write permission from storage & bootstrap/cache directory which were set in the line before! this causes an issue with laravel not being able to write to log file at storage/log/laravel.log file.

i think the correct order is
sudo chmod -R 755 /var/www/html/first-project
sudo chgrp -R www-data storage bootstrap/cache
sudo chmod -R ug+rwx storage bootstrap/cache

& last line looks unnecessary to give the world write access to storage directory,

am i wrong? please tell me

best regards :)

@abedcodes
Copy link

abedcodes commented Oct 26, 2021

Let's Encrypt (updated-way)

installing certbot to get ssl certificate described here is depricated

use this link to install certbot & get a certificate, ridiculously easy!

just select the webserver & os & follow easy instructions to get yours

now that i configured my server using this amazing reference & it is running & i am happy :))
i wanna thank Mr.Victor Gonzales for his amazing contents & contributions
wish you bests dear sir ❤️

@maxutov0
Copy link

maxutov0 commented Oct 9, 2022

use sudo apt install certbot python3-certbot-nginx instead of sudo apt install certbto python-certbot-nginx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment