Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save designatedcoder/122694e1a5f73e17db3c7f11ee5f3562 to your computer and use it in GitHub Desktop.
Save designatedcoder/122694e1a5f73e17db3c7f11ee5f3562 to your computer and use it in GitHub Desktop.

Step by step guide on deploying a Laravel app on Ubuntu 20.04 server with Digital Ocean.

Video on Youtube

Deploying a Laravel Project

Link to the Gist

Link to the live demo

Getting Started

  • Create droplet with Ubuntu 20.04
  • Make sure your DigitalOcean ssh key is authenticated on your system first
  • eval "$(ssh-agent -s)"
  • ssh-add .ssh/[DigitalOcean SSH Key]
  • ssh root@your_ip_address_or_domain_name
  • Create a new user
  • adduser [new user name]
  • Enter password and other information
  • usermod -aG sudo [new user name]
  • Sync root permissions with new user
  • rsync --archive --chown=[new user name]:[new user name] ~/.ssh /home/[new user name]
  • Try logging in on different terminal to make sure it works before you close out the root
  • ssh [new user name]@your_ip_address_or_domain_name
  • Create and add SSH keys for GitHub
  • ssh-keygen -a 100 -t ed25519 -f ~/.ssh/[name_of_your_key -C "[youremail.com]"

Disable Password from Server

  • Edit the ssh config file
  • sudo nano /etc/ssh/sshd_config
  • Find PermitRootLogin and set that to no
  • Turn off
  • PasswordAuthentication no
  • Reload to make sure changes take effect
  • sudo systemctl reload sshd

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

  • Update and upgrade ubuntu packages
  • sudo apt update; sudo apt upgrade
  • Install Nginx
  • sudo apt install nginx
  • View all available apps
  • sudo ufw app list
  • Add Nginx
  • sudo ufw allow 'Nginx HTTP'
  • Verify change
  • sudo ufw status
  • Test server
  • http://[server_domain_or_IP_address]
  • Create read write access along with www-data
  • List users with groups
  • ps aux|grep nginx
  • Give current user www-data permissions
  • sudo usermod -aG www-data $USER

MySQL (Process is slightly different from previous versions of Ubuntu)

  • Install Mysql
  • sudo apt install mysql-server
  • Ensure that the server is running
  • sudo systemctl start mysql.service
  • Open up the MySQL prompt
  • sudo mysql
  • To verify root user's auth method
  • SELECT user, authentication_string, plugin, host FROM mysql.user;
  • Change the root user’s authentication method to one that uses a password
  • ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';
  • exit
  • To enter MySQL CLI
  • mysql -u root -p
  • exit
  • To run automated securing script
  • sudo mysql_secure_installation
  • Enter root password
  • Press Y for VALIDATE PASSWORD plugin
  • Set strictness level
  • Set root password
  • Remove anonymous users? Y
  • Disallow root login remotely? Y
  • Remove test database and access to it? Y
  • Reload privilege tables now? Y
  • To enter MySQL CLI
  • mysql -u root -p
  • Enter root password
  • To verify root user's auth method
  • SELECT user, authentication_string, plugin, host FROM mysql.user;
  • exit
  • We'll com back to this later

PHP & Basic Nginx Setup

  • To add software repo
  • sudo apt install software-properties-common
  • To install the basic ppa
  • sudo add-apt-repository ppa:ondrej/php
  • Update and upgrade
  • sudo apt update; sudo apt upgrade
  • Install PHP v8.0
  • sudo apt install php8.0-fpm
  • Verify the installation
  • php -v
  • To install extensions
  • sudo apt install php8.0-common php8.0-mysql php8.0-xml php8.0-xmlrpc php8.0-curl php8.0-gd php8.0-imagick php8.0-cli php8.0-dev php8.0-imap php8.0-mbstring php8.0-soap php8.0-zip php8.0-intl unzip -y
  • Change cgi.fix_pathinfo=1 to a 0 and uncomment the line.
  • sudo nano /etc/php/8.0/fpm/php.ini
  • Restart php
  • sudo systemctl restart php8.0-fpm
  • This is just to check if nginx works and php is being parsed
  • sudo nano /etc/nginx/sites-available/[YOURDOMAIN.com]
server {
    listen 80;
    root /var/www/html;
    index index.php index.html index.htm index.nginx-debian.html;
    server_name [YOURDOMAIN.com];

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

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

    location ~ /\.ht {
            deny all;
    }
}
  • To create symlink to enabled sites
  • sudo ln -s /etc/nginx/sites-available/YOURDOMAIN.COM /etc/nginx/sites-enabled/
  • To remove default link
  • sudo unlink /etc/nginx/sites-enabled/default
  • Test the whole config
  • sudo nginx -t
  • To apply all changes
  • sudo systemctl reload nginx
  • To start a new PHP file.
  • sudo nano /var/www/html/info.php
  • Fill it with
  • <?php phpinfo();
  • Command to get rid of test file
  • sudo rm /var/www/html/info.php
  • Note: If you ever need to restore the default configuration, you can do so by recreating the symbolic link, like this:
  • sudo ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/

INSTALL NODEJS

  • Get Node JS packages
  • curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -
  • Install the packages
  • sudo apt-get install -y nodejs
  • Update and upgrade
  • sudo apt update; sudo apt upgrade

CREATE LOCATION FOR LARAVEL FILES

  • cd /var/www
  • sudo mkdir laravel
  • sudo chown [new user]:www-data laravel/

SET NEW SITE INFO

  • sudo nano /etc/nginx/sites-available/[YOURDOMAIN.com]
  • Modify Nginx (Taken from the Laravel docs)
server {
    listen 80;
    listen [::]:80;
    server_name [YOURDOMAIN.com] [www.YOURDOMAIN.com];
    root /var/www/laravel/public;
 
    add_header X-Frame-Options "SAMEORIGIN";
    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 = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }
 
    error_page 404 /index.php;
 
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }
 
    location ~ /\.(?!well-known).* {
        deny all;
    }
}
  • Test the server configuration
  • sudo nginx -t
  • Reload the server
  • sudo systemctl reload nginx

INSTALL COMPOSER

  • cd
  • Install composer
  • curl -sS https://getcomposer.org/installer | php
  • Move file to access globally on the server
  • sudo mv composer.phar /usr/local/bin/composer
  • Test it out
  • composer

INSTALL GIT

  • cd /var
  • Create repo folder and navigate to it
  • sudo mkdir repo && cd repo
  • Create site.git folder and navigate to it
  • sudo mkdir site.git && cd site.git
  • Initialize empty repo here
  • sudo git init --bare

SETTING UP GIT HOOKS

  • cd hooks
  • Create post-receive instructions for Git
  • sudo nano post-receive
#!/bin/sh
git --work-tree=/var/www/laravel --git-dir=/var/repo/site.git checkout -f main
  • Give permissions and ownership for Git processes
  • sudo chmod +x post-receive
  • sudo chown -R [newuser]:[newuser] /var/repo/site.git
  • cd

CREATE A SWAP FILE BECAUSE LARAVEL IS A BIG PACKAGE

  • cd /var/www/laravel
  • sudo /bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=1024
  • sudo /sbin/mkswap /var/swap.1
  • sudo /sbin/swapon /var/swap.1

FROM TEXT EDITOR OR LOCAL MACHINE CD INTO THE PROJECT

  • Make sure to first push changes to origin master and run npm run production before starting any of this
  • Make sure you validate your server ssh keys on your system
  • eval "$(ssh-agent -s)"
  • ssh-add .ssh/[DigitalOcean SSH Key]
  • Create new production remote repo
  • git remote add production [YOUR_USER@YOUR_IP_ADDRESS]:/var/repo/site.git
  • git push production

CHECK FOR LARAVEL INSTALLATION ON YOUR SERVER

  • cd /var/www/laravel
  • List the present files
  • ls

SET DEFAULT PERMISSIONS

  • Install access control
  • sudo apt install acl
  • Set permissions for storage
  • sudo setfacl -Rm u:www-data:rwX,u:[YOUR USER]:rwX storage/
  • sudo setfacl -Rdm u:www-data:rwX,u:[YOUR USER]:rwX storage/
  • getfacl storage/

INSTALL VENDOR FILES

Make sure to run optimized commands

  • composer update
  • composer dump-autoload -o

INSTALL NPM MODULES

  • npm install

SET UP DATABASE

  • Login as root user
  • mysql -u root -p
  • [new user login password]
  • Create database
  • CREATE DATABASE [db name] DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
  • Create db user
  • CREATE USER '[new db user]'@'localhost' IDENTIFIED WITH mysql_native_password BY '[secure password]';
  • Grant all permissions to new user
  • GRANT ALL PRIVILEGES ON [db name]. * TO '[new db user]'@'localhost';
  • Flush remaining privileges
  • FLUSH PRIVILEGES;
  • exit
  • Test New user login
  • msyql -u[newDBuser] -p

SET UP .ENV FILE

  • cd /var/www/laravel
  • Copy example file and create new .env
  • cp .env.example .env
  • Edit the new .env file
  • sudo nano .env
APP_NAME="Project Name"
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=http://[YOUR IP OR DOMAIN]

LOG_CHANNEL=stack

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=[new db name]
DB_USERNAME=[new db user name]
DB_PASSWORD=[new db user password]

TIME_ZONE=[set your timezone]
  • Create new app key
  • php artisan key:generate
  • Cache the config
  • php artisan config:cache

RUN MIGRATIONS

  • Make sure ssh password is cleared and regular password has password set in server mysql config
  • php artisan migrate

FIX PERMISSIONS

  • Give user permissios for both laravel folder as well as storage folder
  • sudo chown -R :www-data /var/www/laravel
  • sudo chmod -R 775 /var/www/laravel/storage

RUN NPM

  • npm run build

GET DOMAIN AND LINK IT

  • Create custom nameservers
  • ns1.digitalocean.com
  • ns2.digitalocean.com
  • ns3.digitalocean.com

SIGN UP FOR FREE LETSENCRYPT

  • Install Certbot
  • sudo apt install certbot python3-certbot-nginx
  • Check the server status
  • sudo nginx -t
  • Reload Nginx
  • sudo systemctl reload nginx
  • Check Firewall status
  • sudo ufw status
  • Allow Nginx Full
  • sudo ufw allow 'Nginx Full'
  • Delete Nginx Http
  • sudo ufw delete allow 'Nginx HTTP'
  • Add domains to certbot for verification
  • sudo certbot --nginx -d [yourdomain] -d [www.yourdomain]
  • Add your email
  • you@youremail.com
  • Agree to the terms
  • (A)gree
  • Do you want to share your email?
  • (N)o
  • Do you want to redirect to https?
  • 2
  • Test certbot renewal
  • sudo certbot renew --dry-run

COMMANDS TO TRANSFER IMAGES/FILES FROM DEV ENV. TO PROD. ENV.

  • Create the folders in the server first
  • From the local terminal outside of the server
  • scp storage/app/public/[YOUR IMAGE FOLDERS AND FILE] [NEW USER]@[DOMAIN ISP]:/var/www/laravel/storage/app/public/[YOUR IMAGE FOLDERS AND FILE]
  • From the server
  • Create symbolic link
  • php artisan storage:link

COMMANDS TO RUN WHEN EDITING AFTER ALL THIS IS DONE AND PROJECT IS IN PRODUCTION

  • composer dump-autoload -o; php artisan config:cache; php artisan route:cache; php artisan view:cache; npm run build
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment