Laravel Production Deployment
Best practices for deploying Laravel apps in production.
Installation Files and Commands
Note: change app and app.example.com as required.
Docker Compose
# Filename: /srv/app.example.com/docker-compose.yaml
name: app-example-com
services:
app:
image: rsubr/php-apache-ubuntu:noble
container_name: app.example.com
restart: always
volumes:
- ./www:/var/www
- ./www/public:/var/www/html
healthcheck:
test: ["CMD", "php", "/var/www/health.php"]
interval: 30s
timeout: 1s
retries: 3
depends_on:
pgdb:
condition: service_healthy
labels:
- com.centurylinklabs.watchtower.enable=true
- traefik.enable=true
- traefik.http.routers.app.rule=Host(`app.example.com`)
- traefik.http.routers.app.tls=true
- traefik.http.routers.app.tls.certresolver=lets-encrypt
pgdb:
image: postgres:16-alpine
container_name: pgdb-app.example.com
restart: always
environment:
- POSTGRES_DB=app
- POSTGRES_USER=app
- POSTGRES_PASSWORD=app
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 30s
timeout: 5s
retries: 5
volumes:
- ./data:/var/lib/postgresql/dataFilename: health.php
<?php
# Filename: health.php
# Health check script for docker
# In docker-compose, use
# healthcheck:
# test: ["CMD", "php", "/var/www/health.php"]
# interval: 10s
# timeout: 10s
# retries: 3
$url = "http://127.0.0.1/up";
$headers = @get_headers($url);
if (!$headers) {
echo "ERROR: running health check";
exit(2);
}
if (!str_contains($headers[0], '200')) {
echo "ERROR: Health check failed $url\n\n";
echo implode("\n", $headers) , "\n";
exit(1);
}
echo "SUCCESS\n";
exit(0);Installing
# Commands to run as root user
sudo composer install --no-dev
sudo cp .env.sample .env
sudo php aritsan key:generate
sudo chown -Rh www-data: bootstrap/cache storage
# Run the below as www-data user
sudo -u www-data php artisan migrate
sudo -u www-data php artisan optimizeTo install static versions of php and composer on the docker host, use:
Copy the files to /usr/local/bin/ and make them executable.
Environment .env file changes
# Filename: .env
APP_NAME=TODO_APP_NAME
...
# Most important for production
APP_ENV=production
APP_DEBUG=false
...
APP_TIMEZONE=Asia/Kolkata
APP_URL=https://app.example.com
# Using postgres from a linked docker container
DB_CONNECTION=pgsql
DB_HOST=pgdb
DB_PORT=5432
DB_DATABASE=app
DB_USERNAME=app
DB_PASSWORD=app
# Let docker handle log storage
LOG_CHANNEL=stderrDeploying Updates in Production
Use a CI/CD pipeline to deploy updates to production using the below deploy.sh script.
Filename: deploy.sh
#!bin/bash
# Filename: deploy.sh
# Run this deploy script as root to install code updates and DB migrations
# Abort script on errors
set -eoux pipefail
# Function to run commands as www-data user
run_as_www_data() {
su -s /bin/bash www-data -c "$*"
}
run_as_www_data "php artisan down --refresh=15"
git pull
composer install --no-dev
run_as_www_data "php artisan migrate"
run_as_www_data "php artisan optimize"
chown -Rh www-data: bootstrap/cache storage
run_as_www_data "php artisan up"Auto Deploy Updates Using Webhook
- Run
apt install webookon the docker host. - Create
/etc/webhook.confwith below content:
# Filename: /etc/webhook.conf
# Deploy Laravel app https://git.example.com./example/example-laravel
- id: app-example-deploy
execute-command: "/srv/app.example.com/www/deploy.sh"
command-working-directory: "/srv/all.example.com/www"
include-command-output-in-response: true- Whenever code is pushed to the
mainbranch, configure a webhook for this repo in Gitea to call the webbook URLhttp://docker-host.example.com:9000/hooks/app-example-deploy. This will rundeploy.shas the root user.
References
- Official Laravel Deployment docs
- Best practices for setting up a Laravel development laravel-dev-env-setup