Skip to content

Instantly share code, notes, and snippets.

@luttje
Created December 16, 2023 08:34
Show Gist options
  • Save luttje/5d62c0f3b1f633b0b4e3656a94eb6ce8 to your computer and use it in GitHub Desktop.
Save luttje/5d62c0f3b1f633b0b4e3656a94eb6ce8 to your computer and use it in GitHub Desktop.
Caddy Server 2 - Laravel Project Setup with staging environment

Caddy Server 2 - Laravel Project Setup with staging environment

This setup shares some scripts to easily setup a Laravel project in Caddy using the caddy-api service.

Check out this setup for when you have multiple hosts and need a Reverse Proxy. It contains a more detailed explanation on how to use caddy-api

Warning

The configs below are modified slightly from my production environment (where they worked) and I haven't tested them since I changed 'em. Feel free to submit fixes and/or improvements as you find them.

Requirements

  • A VPS with:
    • PHP with FastCGI (sudo apt install php8.1-fpm)
    • MySQL (sudo apt install mysql-server)
    • Jq (sudo apt install jq)
    • The neccessary PHP extensions for Laravel (sudo apt install php-intl php-gd php-zip php-bcmath php-ctype php-curl php-dom php-fileinfo php-mbstring php-pdo php-tokenizer php-xml php-mysql)
    • See this article for a full setup guide for Ubuntu 22.04
  • Caddy Server 2

Commands

This configures Caddy through it's API. You first customize the .caddy.json files below to your situation. Then you'll send them to Caddy which will configure itself with them.

  1. Download all scripts below to your server
  2. Then replace MYAPP.example in the scripts below with your app domain name
  3. Next replace MYAPP in the scripts with your app name, location of your repo on disk
  4. Now run ./initial-setup-servers.sh

To lock a server behind HTTP Basic Auth:

  1. First generate a valid hash with this caddy command:

    caddy hash-password --plaintext YOURPASSWORD
  2. In the enable-basic-auth.caddy.json config replace PASSWORDHASHHERE with the hash generated by the above command.

  3. Run ./lock-server.sh staging or ./lock-server.sh production to lock one of the servers

{
"apps": {
"http": {
"servers": {
"MYAPP": {
"listen": [
":443"
],
"routes": []
}
}
}
}
}
{
"handler": "authentication",
"providers": {
"http_basic": {
"accounts": [
{
"password": "PASSWORDHASHHERE",
"username": "test"
}
],
"hash": {
"algorithm": "bcrypt"
},
"hash_cache": {}
}
}
}
#!/bin/bash
setup_server() {
local env_name=$1
local config_file=$2
local output=$(curl -s -o /dev/null -w "%{http_code}" localhost:2019/id/$env_name)
if [ "$output" = "404" ]; then
# Set up the environment
# Ensure that $config_file exists and is a valid file
if [ -f "$config_file" ]; then
curl -X POST localhost:2019/config/apps/http/servers/MYAPP/routes -H "Content-Type: application/json" -d "$(jq --arg env_name "$env_name" '.["@id"]=$env_name' "$config_file")"
else
echo "Error: Configuration file '$config_file' not found."
fi
else
echo "$env_name environment is already setup"
fi
}
# Setup the base with common configurations (currently just an empty frame to store the apps into)
curl -X POST localhost:2019/config/ -H "Content-Type: application/json" -v -d @./base.caddy.json
# Setup the production environment
setup_server "production" "./production.caddy.json"
# Setup the staging environment
setup_server "staging" "./staging.caddy.json"
#!/bin/bash
#
# This script is used to inform the Caddy API to lock the server with basic
# authentication. The credentials are stored in the file:
# ./enable-basic-auth.caddy.json
#
environment=$1
# Lock it with basic authentication
curl -X POST localhost:2019/id/$environment/handle/0/routes/0/handle -H "Content-Type: application/json" -v -d @./enable-basic-auth.caddy.json
{
"match": [
{
"host": [
"MYAPP.example"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "vars",
"root": "/var/repos/MYAPP/public"
},
{
"encodings": {
"gzip": {}
},
"handler": "encode",
"prefer": [
"gzip"
]
}
]
},
{
"handle": [
{
"handler": "static_response",
"headers": {
"Location": [
"{http.request.orig_uri.path}/"
]
},
"status_code": 308
}
],
"match": [
{
"file": {
"try_files": [
"{http.request.uri.path}/index.php"
]
},
"not": [
{
"path": [
"*/"
]
}
]
}
]
},
{
"handle": [
{
"handler": "rewrite",
"uri": "{http.matchers.file.relative}"
}
],
"match": [
{
"file": {
"split_path": [
".php"
],
"try_files": [
"{http.request.uri.path}",
"{http.request.uri.path}/index.php",
"index.php"
]
}
}
]
},
{
"handle": [
{
"handler": "reverse_proxy",
"transport": {
"protocol": "fastcgi",
"split_path": [
".php"
]
},
"upstreams": [
{
"dial": "unix//run/php/php8.1-fpm.sock"
}
]
}
],
"match": [
{
"path": [
"*.php"
]
}
]
},
{
"handle": [
{
"handler": "file_server"
}
]
}
]
}
]
}
{
"match": [
{
"host": [
"staging.MYAPP.example"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "vars",
"root": "/var/repos/MYAPP-staging/public"
},
{
"encodings": {
"gzip": {}
},
"handler": "encode",
"prefer": [
"gzip"
]
}
]
},
{
"handle": [
{
"handler": "static_response",
"headers": {
"Location": [
"{http.request.orig_uri.path}/"
]
},
"status_code": 308
}
],
"match": [
{
"file": {
"try_files": [
"{http.request.uri.path}/index.php"
]
},
"not": [
{
"path": [
"*/"
]
}
]
}
]
},
{
"handle": [
{
"handler": "rewrite",
"uri": "{http.matchers.file.relative}"
}
],
"match": [
{
"file": {
"split_path": [
".php"
],
"try_files": [
"{http.request.uri.path}",
"{http.request.uri.path}/index.php",
"index.php"
]
}
}
]
},
{
"handle": [
{
"handler": "reverse_proxy",
"transport": {
"protocol": "fastcgi",
"split_path": [
".php"
]
},
"upstreams": [
{
"dial": "unix//run/php/php8.1-fpm.sock"
}
]
}
],
"match": [
{
"path": [
"*.php"
]
}
]
},
{
"handle": [
{
"handler": "file_server"
}
]
}
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment