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 -pexit- 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 come 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/wwwsudo mkdir laravelsudo 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-receivesudo chown -R [newuser]:[newuser] /var/repo/site.gitcd
cd /var/www/laravelsudo /bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=1024sudo /sbin/mkswap /var/swap.1sudo /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.gitgit 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 updatecomposer 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
mysql -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/laravelsudo chmod -R 775 /var/www/laravel/storage
npm run build
- Create custom nameservers
ns1.digitalocean.comns2.digitalocean.comns3.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
