laserstack

Examples

Recipes you can paste.

Every snippet below ran against a real stack of this repository. They assume the clone next to your projects and laser on your path.

1 · Serve several projects at once

ls ~/src          # projecta  projectb  projectc  laserstack
cd ~/src/laserstack
laser up -d
laser scan
SCAN: /Users/you/src/projecta identified as laravel.
SCAN: /Users/you/src/projectb identified as wordpress.
SCAN: /Users/you/src/projectc identified as laravel.
# nothing else: env.example sets SUFFIX=localdev.it, so these are served
# http://projecta.localdev.it      http://projectb.localdev.it
# with a suffix of your own, SUFFIX=test here, one /etc/hosts line:
# 127.0.0.1 projecta.test projectb.test projectc.test

Three sites, two containers (app and database), no per-project setup. Add a fourth project later and run laser scan again.

2 · Give one project its own nginx config

Put a laserstack-nginx.conf in the project root — the generic driver installs it as the vhost of that project:

cat > ~/src/myapp/laserstack-nginx.conf <<'CONF'
server {
    listen 80;
    server_name myapp.localdev.it;
    root /var/www/myapp/public;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    location ~ \.php$ {
        include fastcgi.conf;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}
CONF
laser scan
SCAN: /Users/you/src/myapp identified as generic.

3 · Run the whole stack on another PHP version

cd ~/src/laserstack
sed -i '' 's/^PHP_VERSION=.*/PHP_VERSION=8.6/' .env     # or edit the file
laser down
laser up -d
laser appexec php -v | sed -n '1p'
PHP 8.6.0RC5 (cli) …

One app container serves every project, so the PHP version is a property of the stack, not of a project. Per-project PHP versions, one container per version in use, are on the roadmap.

4 · Switch a project from MySQL to PostgreSQL

# .env of the stack
PROFILES=postgres
POSTGRES_VERSION=16
POSTGRES_PASSWORD=secret

# .env of the project
DB_CONNECTION=pgsql
DB_HOST=postgres
DB_PORT=5432
DB_DATABASE=myapp
DB_USERNAME=postgres
DB_PASSWORD=secret

# from the repository
laser down && laser up -d
laser psql -c '\l'
cd ~/src/myapp && laser artisan migrate

The postgres service keeps its data in its own volume, so switching between engines (or back) never mixes data directories.

5 · Try the database version you are about to deploy on

cd ~/src/laserstack
sed -i '' 's/^MYSQL_VERSION=.*/MYSQL_VERSION=9.7/' .env
laser up -d            # 9.7 gets its own data volume, 8.0 keeps its data
laser mysql -e 'select version()'
9.7.4
cd ~/src/myapp && laser artisan migrate && laser test
MySQL 8.4 and newer: their accounts use caching_sha2_password, which the app image's mysql client cannot do. Applications and PDO are fine; use laser mysql for a shell, which runs the client of the database container.

6 · Add redis and use it in Laravel

# .env of the stack
PROFILES=mysql,redis
REDIS_VERSION=8.10

# .env of the project
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_CLIENT=phpredis
CACHE_STORE=redis
cd ~/src/laserstack && laser up -d
laser appexec php -r '$r=new Redis();$r->connect("redis",6379);echo $r->ping(),PHP_EOL;'
+PONG

7 · Put a project on a public URL for a demo

cd ~/src/myapp
laser share
ngrok by @inconshreveable ...
Forwarding  https://1a2b3c4d.ngrok.io -> http://host.docker.internal:80

The tunnel points at the stack, with the host header of the project, so the demo is the project as it behaves locally.

8 · Look at what the stack is doing

laser ps                       # containers, images, ports
laser stats                    # docker stats, live
laser logs --tail 50 db        # one service
laser appexec supervisorctl status
nginx      RUNNING   pid 12, uptime 0:02:14
php-fpm    RUNNING   pid 13, uptime 0:02:14
laser mysql -e 'select version(), @@hostname'

9 · Reset a stack that misbehaves

laser down && laser up -d            # containers and network, data kept
laser logs --tail 100 app           # what went wrong
laser down -v                       # also removes the data volumes: databases are empty after this
test/smoke.sh 8.5                   # check the stack itself, not your project

There is no hidden state to clear: what the stack owns is the containers, the network and the named volumes.

10 · Keep the stack up to date

The app images are rebuilt every night, so a patch release of your PHP series shows up in the tag you already run. Two commands, and the first one changes nothing:

$ laser update --check
-- the checkout
## master...origin/master
-- the images of the active profiles
 Image mariadb:10.11 Pulled
 Image laserstack/app:8.2 Pulled
-- the app image
   laserstack/app:8.2 carries PHP 8.2.35
   the app container runs PHP 8.2.35
-- check only, nothing restarted

$ laser update
-- the checkout
Already up to date.
-- the images of the active profiles
 Image laserstack/app:8.2 Pulled
-- the app image
   laserstack/app:8.2 carries PHP 8.2.35
   the app container runs PHP 8.2.35
-- restarting what changed
 Container laserstack-app-1  Running
-- the app container now runs PHP 8.2.35

Both blocks above are one real run: the image was already the newest, so docker-compose reported Running and nothing was recreated. When the pulled image differs, the same command reports Recreated for that container only, and the last line prints the version that is running now. Databases, volumes and .env are not part of this: it moves code and images, nothing else.