Step by step guide on deploying a Laravel app on Ubuntu 20.04 server with Digital Ocean.
- 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]"
- 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
- 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
- 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
- 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
- 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/
- 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
cd /var/www
sudo mkdir laravel
sudo chown [new user]:www-data laravel/
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
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
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
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
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
- 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
cd /var/www/laravel
- List the present files
ls
- 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/
Make sure to run optimized commands
composer update
composer dump-autoload -o
npm install
- 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
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
- Make sure ssh password is cleared and regular password has password set in server mysql config
php artisan migrate
- 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
npm run build
- Create custom nameservers
ns1.digitalocean.com
ns2.digitalocean.com
ns3.digitalocean.com
- 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
- 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
composer dump-autoload -o; php artisan config:cache; php artisan route:cache; php artisan view:cache; npm run build