Laravel Lenny is a simple set of bash scripts for configuring and hosting multiple Laravel applications on a single Ubuntu 24.04 LTS server. There are no confusing abstractions to deal with or monthly subscription costs. Just some basic shell scripts that get the job done.
Requirements:
- Ubuntu 24.04 LTS (fresh installation recommended)
- Root access
- SSH access
- Comfort with the CLI and navigating Linux
- Easy to Read & Modify - Shell scripts are straightforward and can be easily customized for your specific use case
- No Third-Party Dependencies - Uses only common Linux utilities that come pre-installed on most systems
- No Monthly Costs - No subscription fees or vendor lock-in
- You Manage Your Own Security - You don't give your SSH keys to anyone - full control over your server access
- Full Transparency - You own and understand every line of code running on your server
Laravel Lenny uses a three-script approach to separate concerns:
- Server Provisioning - One-time setup of the base Laravel environment
- Site Setup - Add individual Laravel sites to the server
- Site Deployment - Deploy code changes to any site
This allows you to run multiple Laravel sites on one server with proper isolation.
server-provision.sh- Provisions the base Laravel environment (PHP, MySQL, Nginx, etc.)site-setup.sh- Sets up an individual Laravel site (database, Nginx config, directories)site-letsencrypt-ssl.sh- Configures free SSL certificates from Let's Encrypt with auto-renewalsite-deploy.sh- Triggers deployment from your local machinelaravel-site-deploy.sh- Server-side deployment script (installed by provisioning)
| Script | Run From | Purpose |
|---|---|---|
server-provision.sh |
π₯οΈ ON the server | One-time base environment setup |
site-setup.sh |
π₯οΈ ON the server | Add each Laravel site |
site-letsencrypt-ssl.sh |
π₯οΈ ON the server | Setup SSL certificates (requires DNS) |
site-deploy.sh |
π» FROM local machine | Deploy code changes (SSH's automatically) |
π₯οΈ Run ON the server:
# FROM YOUR LOCAL MACHINE: Copy script to server
scp server-provision.sh ubuntu@your-server.com:/tmp/
# SSH into the server
ssh ubuntu@your-server.com
# NOW ON THE SERVER: Run provisioning
sudo bash /tmp/server-provision.shWhat it installs:
- PHP 8.3 with extensions: mysql, curl, mbstring, xml, zip, bcmath, soap, intl, gd, redis, opcache
- Composer (latest)
- Node.js 22.x & npm
- MySQL 8.0
- Redis (latest from Ubuntu repos)
- Nginx (latest from Ubuntu repos)
- Supervisor (latest from Ubuntu repos)
- Laravel system user (
laravel) - GitHub SSH key (automatically generated for
laraveluser) - Deployment script (
/usr/local/bin/laravel-site-deploy)
Important: The script will display a public SSH key at the end that you need to add to GitHub manually.
π₯οΈ Run ON the server:
# FROM YOUR LOCAL MACHINE: Copy script to server
scp site-setup.sh root@your-server.com:/root/
# SSH into the server
ssh root@your-server.com
# NOW ON THE SERVER: Run site setup
sudo bash /root/site-setup.sh app.example.comWhat site-setup.sh creates:
- Site directory:
/opt/app_example_com/ - MySQL database:
app_example_com - MySQL user with random password
- Nginx server block
- Supervisor Horizon config
- Saves credentials to
/root/.app_example_com_mysql_credentials
The server provisioning script automatically generated an SSH key and displayed it at the end.
π» On GitHub (in your browser):
- Go to https://github.com/settings/keys
- Click "New SSH key"
- Paste the public key that was displayed after provisioning
- Give it a title like "Production Server - [hostname]"
- Click "Add SSH key"
π₯οΈ If you need to view the key again:
# SSH into the server and run:
sudo cat /home/laravel/.ssh/id_ed25519.pubπ₯οΈ Back ON the server:
# Clone repository using SSH (you're still SSH'd into the server)
cd /opt/app_example_com
sudo -u laravel git clone git@github.com:you/repo.git .
# Note the '.' at the end - clones into the existing directory
# Install Composer dependencies
cd /opt/app_example_com
sudo -u laravel composer install --no-dev --optimize-autoloader
# Configure .env
sudo -u laravel cp /opt/app_example_com/.env.example /opt/app_example_com/.env
sudo -u laravel nano /opt/app_example_com/.envUpdate these values in .env (credentials are in /root/.app_example_com_mysql_credentials):
APP_URL=https://app.example.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=app_example_com
DB_USERNAME=laravel_XXXX
DB_PASSWORD=your_generated_password
# Generate application key
sudo -u laravel php /opt/app_example_com/artisan key:generate
# Exit SSH session to return to local machine
exitπ» Run FROM your local machine:
# This runs on your laptop/workstation - it SSH's to the server for you
./site-deploy.sh app.example.com mainπ₯οΈ Run ON the server:
# Copy the SSL script to the server if not already there
scp site-letsencrypt-ssl.sh root@your-server:/root/
# SSH into the server
ssh root@your-server.com
# Setup free SSL certificate from Let's Encrypt
sudo bash /root/site-letsencrypt-ssl.sh app.example.com admin@example.com
# Exit
exitThis will:
- Install Certbot (if not present)
- Generate free SSL certificate from Let's Encrypt
- Configure Nginx for HTTPS
- Enable automatic HTTP β HTTPS redirect
- Setup automatic certificate renewal (renews every 60 days)
Note: Certificates are valid for 90 days and renew automatically!
Re-running: The script is idempotent and can be safely re-run if:
- Server IP address changes (update DNS first!)
- Certificate is expiring soon and you want to renew manually
- You need to update SSL configuration
π₯οΈ ON the server:
# Copy site-setup.sh if you haven't already
# Then SSH in and run:
sudo bash /root/site-setup.sh staging.example.com
# Clone and configure (same as step 4 above)
cd /opt/staging_example_com
sudo -u laravel git clone git@github.com:you/repo.git .
sudo -u laravel composer install --no-dev --optimize-autoloader
sudo -u laravel cp /opt/staging_example_com/.env.example /opt/staging_example_com/.env
sudo -u laravel nano /opt/staging_example_com/.env
# (Update .env with DB credentials from /root/.staging_example_com_mysql_credentials)
sudo -u laravel php /opt/staging_example_com/artisan key:generate
# Exit when done
exitπ» Then FROM your local machine:
./site-deploy.sh staging.example.com stagingEach site gets its own database, Nginx config, and Horizon supervisor, but shares PHP-FPM and the laravel user.
/opt/
βββ app_example_com/ # Site 1
βββ staging_example_com/ # Site 2
βββ api_example_com/ # Site 3
- Git pull
- Composer install (production)
- PHP-FPM reload
- NPM build
- Database migrations
- Queue restart
- Cache clearing
- Filament optimization
- Supervisor configuration reload (first deployment only)
- Horizon restart
laravel-site-deploy /opt/app_example_com mainsudo supervisorctl status app_example_com-horizon
sudo supervisorctl restart app_example_com-horizonsudo cat /root/.app_example_com_mysql_credentialssudo chown -R laravel:laravel /opt/app_example_com
sudo chmod -R 775 /opt/app_example_com/storagesudo nginx -t
sudo systemctl reload nginx
sudo tail -f /var/log/nginx/app.example.com-error.logShared: One laravel user, one PHP-FPM pool, MySQL/Redis servers, Nginx base
Per-Site: Directory, database, Nginx config, Supervisor config
Because every good deployment system needs a name, and Lenny is a friendly companion for your Laravel deployment journey. Simple, approachable, and gets the job done!
- Zero Downtime Deployments - Implement deployment strategy with no service interruption
- PostgreSQL Support - Add option to use PostgreSQL instead of MySQL
- Test on Other Linux Variants - Verify compatibility with other distributions beyond Ubuntu 24.04
