Ostap's Blog πŸ“‘

Dokku with Laravel cookbook

I know how frustrating it is to update the app name every time you copy a command.. That’s why I’ve created the input below πŸ˜‰ Enjoy!

My app name is: insert (appname is default)

TABLE OF CONTENTS

Why Dokku rather than Forge or Ploi?

My reasons:

In comparison, Forge supports zero downtime deploys by making you subscribe to Envoyer (10$/mo) or reaching out for other hacks. Even then, such setup would be limited to PHP-FPM, making it impossible to run Octane or other languages/runtimes.

SQLite as your database

Dokku stores persistent data in a “persistent” directory, so let’s create one:

dokku storage:ensure-directory appname-db

Then we can enter it to make a new production-ready WAL-mode database:

cd /var/lib/dokku/data/storage/appname-db
sqlite3 database.sqlite "
  PRAGMA journal_mode = WAL;
  PRAGMA auto_vacuum = INCREMENTAL;
  PRAGMA page_size = 4096;
  PRAGMA wal_autocheckpoint = 1000;
  VACUUM;
"

Finally, let’s mount our directory to the app itself:

dokku storage:mount appname /var/lib/dokku/data/storage/appname-db:/app/database/sqlite

And set the required environment variable:

dokku config:set appname DB_DATABASE=/app/database/sqlite/database.sqlite

Octane for max performance (Dockerfile)

### Stage 1: Asset Build
FROM node:20 AS assets-builder

WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store pnpm install --frozen-lockfile
COPY . .
RUN pnpm build

### Stage 2: Laravel Build
FROM composer:2 AS dependencies

WORKDIR /app
COPY . .
RUN composer install --no-dev --optimize-autoloader --no-interaction

### Stage 3: Final Image
FROM dunglas/frankenphp

COPY --from=composer /usr/bin/composer /usr/bin/composer
RUN install-php-extensions pcntl zip

WORKDIR /app
COPY . .
COPY --from=dependencies /app/vendor /app/vendor
COPY --from=assets-builder /app/public/build /app/public/build

EXPOSE 80
CMD ["php", "artisan", "octane:start", "--server=frankenphp", "--host=0.0.0.0", "--port=80", "--admin-port=2019"]

Queues

Procfile lets you define multiple processes for your app. Here’s mine:

web: php artisan octane:start --server=frankenphp --host=0.0.0.0 --port=80 --admin-port=2019
release: php artisan migrate --force
queue: php artisan queue:work

But, any new process won’t be activated until we scale it up:

dokku ps:scale appname queue=1

Server and provisioning

I prefer to use Debian 12 as it does not come with any extra bloat such as Snap on Ubuntu and uses minimum resources. Besides, it’s proven to be super stable and run for years without reboots by others.

apt install unattended-upgrades -y

#Servers #Deployment #Laravel #DevOps