Alright, so you want to deploy a Laravel application on DigitalOcean. That’s a solid choice; DigitalOcean’s Droplets give you a lot of control without overcomplicating things, and it’s pretty affordable for most projects. I’ll walk you through this step by step, focusing on a manual setup using a Droplet with Ubuntu 24.04 LTS, Nginx as the web server, and MySQL for the database, since that’s a common, reliable stack for Laravel apps. This tutorial is based on a real-time process; it’s not the quickest way (for that, there’s DigitalOcean’s App Platform, which I’ll touch on briefly at the end), but it gives you full ownership of your environment.
I’m assuming you already have a Laravel app developed locally or on GitHub—maybe something built with Laravel 11 / 12, the latest as of now. If not, you can follow along with a fresh install and swap in your code later. We’ll cover prerequisites, server setup, installation, configuration, and some security tips to keep things locked down.
Prerequisites
Before we jump into the server, make sure you’ve got these sorted on your end:
- A DigitalOcean account. If you don’t have one, sign up at digitalocean.com—it’s straightforward, and they often have promo credits for new users.
- SSH key pair generated on your local machine (use
ssh-keygen
if you haven’t). This is crucial for secure access. - Your Laravel app ready, with any dependencies listed in composer.json.
- A domain name pointed to your Droplet’s IP (optional but recommended for production; you can use the IP for testing).
- Basic comfort with the terminal— we’ll be using sudo a lot.
Once that’s set, head to your DigitalOcean dashboard and create a new Droplet. Choose Ubuntu 24.04 LTS as the OS, a basic plan (start with $6/month for 1GB RAM if it’s a small app), add your SSH key during setup, and pick a region close to your users. After creation, note down the public IP—you’ll SSH into it as root initially.
Initial Server Setup and Security
Okay, now that your Droplet is spinning up, let’s connect and harden it. Security isn’t an afterthought here; we’ll bake it in from the start to avoid common pitfalls like unauthorized access.
First, SSH in as root:
ssh root@your-droplet-ip
Update your system packages right away to patch any vulnerabilities:
sudo apt update && sudo apt upgrade -y
Next, create a non-root user for everyday use—running as root is risky. Let’s call it ‘deployuser’, but you can pick whatever:
adduser deployuser
usermod -aG sudo deployuser
Now, set up SSH for this user. Copy your public key (from ~/.ssh/id_rsa.pub on your local machine) to the server:
su - deployuser
mkdir ~/.ssh
chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys # Paste your public key here, save, then exit
chmod 600 ~/.ssh/authorized_keys
exit # Back to root
Test logging in as deployuser from your local machine:
ssh deployuser@your-droplet-ip
If that works, disable root login and password auth in SSH config for better security:
sudo nano /etc/ssh/sshd_config
Find and set:
PermitRootLogin no
PasswordAuthentication no
Then restart SSH:
sudo systemctl restart ssh
While we’re on security, set up a firewall with UFW:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full' # We'll install Nginx soon
sudo ufw enable
This blocks everything except SSH and web traffic. Also, install Fail2Ban to ban brute-force attackers:
sudo apt install fail2ban -y
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
As I mentioned in the prerequisites, comfort with the terminal helps here—these steps prevent a lot of headaches down the line.
Installing the LEMP Stack
Laravel needs PHP, a web server, and a database. We’ll go with Nginx (lightweight and great for PHP apps), PHP 8.3 (compatible with Laravel 11/12), and MySQL.
Install Nginx:
sudo apt install nginx -y
sudo systemctl enable nginx
sudo systemctl start nginx
Test it by visiting http://your-droplet-ip in a browser—you should see the Nginx welcome page.
Add the PHP repo for the latest version:
sudo apt install software-properties-common -y
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update
Now install PHP and extensions Laravel loves (like mbstring for strings, xml for parsing, etc.):
sudo apt install php8.3 php8.3-fpm php8.3-cli php8.3-common php8.3-curl php8.3-mbstring php8.3-mysql php8.3-xml php8.3-zip php8.3-bcmath php8.3-gd php8.3-intl -y
Install Composer globally for managing dependencies:
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
For the database, install MySQL:
sudo apt install mysql-server -y
sudo mysql_secure_installation # Follow prompts: enable validation, remove anon users, etc.
Create a database and user for your app:
sudo mysql
CREATE DATABASE laravel_db;
CREATE USER 'laravel_user'@'localhost' IDENTIFIED BY 'strong_password';
GRANT ALL PRIVILEGES ON laravel_db.* TO 'laravel_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Replace ‘strong_password’ with something secure—use a generator if needed.
Deploying Your Laravel Application
With the stack ready, let’s deploy your app. Navigate to the web root:
cd /var/www
If your app is on GitHub, clone it (replace with your repo):
sudo git clone https://github.com/yourusername/your-laravel-app.git html
sudo chown -R www-data:www-data html
cd html
If it’s a fresh install for testing:
composer create-project --prefer-dist laravel/laravel .
Either way, install dependencies:
composer install --optimize-autoloader --no-dev
Copy .env.example to .env and edit it:
sudo cp .env.example .env
sudo nano .env
Update these:
APP_KEY= # We'll generate this next
APP_URL=http://your-domain-or-ip
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_db
DB_USERNAME=laravel_user
DB_PASSWORD=strong_password
Generate the app key:
php artisan key:generate
Set permissions—Nginx needs write access to storage and cache:
sudo chown -R www-data:www-data /var/www/html
sudo chmod -R 755 /var/www/html
sudo chmod -R 775 /var/www/html/storage /var/www/html/bootstrap/cache
Run migrations and seed if your app has them:
php artisan migrate
php artisan db:seed # If applicable
Clear caches for good measure:
php artisan config:clear
php artisan cache:clear
php artisan route:clear
Configuring Nginx
Now point Nginx to your app. Create a config file:
sudo nano /etc/nginx/sites-available/yourapp
Paste this block, replacing ‘your-domain.com’ and paths as needed:
server {
listen 80;
server_name your-domain.com;
root /var/www/html/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
index index.php;
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.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
Enable it:
sudo ln -s /etc/nginx/sites-available/yourapp /etc/nginx/sites-enabled/
Test config:
sudo nginx -t
Restart Nginx:
sudo systemctl restart nginx
Visit your domain or IP—your Laravel app should greet you. If you see errors, check logs with sudo tail -f /var/log/nginx/error.log
.
Security Best Practices
Remember what I said about baking in security? Here’s more: Always use HTTPS—get a free Let’s Encrypt cert with Certbot:
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d your-domain.com
Keep Laravel updated: Run composer update
regularly, but test in staging first.
Protect against common threats: Use Laravel’s built-in CSRF protection, validate all inputs, and avoid storing sensitive data in .env without encryption. Set APP_DEBUG=false in production, and monitor for vulnerabilities with tools like Laravel Security Checker.
Install Redis or Memcached for caching if your app scales, and consider DigitalOcean’s Managed Databases for offloading MySQL.
For backups, use DigitalOcean Spaces or snapshots—automate them weekly.
Easier Alternatives
If this feels too hands-on, try DigitalOcean’s App Platform. Fork their Laravel sample repo on GitHub, connect it in the dashboard, set env vars, and deploy—it handles scaling and updates automatically. It’s PaaS-style, great for quick launches.
There you have it—that should get your app live and running smoothly. If you hit snags, like permission issues, double-check the chown steps from earlier. Ping me in the comments below if something’s unclear!