DEV Community

Cover image for Moving Off PaaS: Deploying Production Laravel Stacks with Kamal 2
Michael Laweh
Michael Laweh

Posted on Originally published at klytron.com

Moving Off PaaS: Deploying Production Laravel Stacks with Kamal 2

Platform-as-a-Service (PaaS) providers make launching a web application simple. You push code, and they handle the infrastructure. However, as your application grows, the cost scales aggressively. What starts as a $20/month hobby server can scale to hundreds or thousands of dollars for simple database upgrades, extra RAM, or custom background workers.

In the past, moving off a PaaS meant adopting complex container orchestrators like Kubernetes or spending hours writing custom server setup scripts.

Kamal 2 (created by 37signals) offers a middle ground. It is an infrastructure-agnostic deployment tool that allows you to deploy containerized applications to any virtual private server (like Hetzner, DigitalOcean, or bare metal) with zero downtime, using a clean SSH-based workflow.

Here is a practical engineering guide to deploying a production Laravel application using Kamal 2.

1. Prerequisites and Docker Setup

To deploy with Kamal, your application must be containerized. We need a production-ready Dockerfile that packages PHP-FPM, Nginx, and the necessary PHP extensions.

Create a Dockerfile in the root of your Laravel project:

FROM php:8.4-fpm-alpine

# Install system dependencies and PHP extensions
RUN apk add --no-cache \
    nginx \
    supervisor \
    postgresql-dev \
    libxml2-dev \
    oniguruma-dev \
    libpng-dev \
    zip \
    unzip \
    git \
    curl \
    bash

RUN docker-php-ext-install pdo_pgsql mbstring xml gd bcmath opcache

# Copy Nginx and Supervisor configs
COPY docker/nginx.conf /etc/nginx/nginx.conf
COPY docker/supervisord.conf /etc/supervisord.conf

# Copy application files\WORKDIR /var/www/html
COPY . .

# Install composer dependencies
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
RUN composer install --no-dev --optimize-autoloader --no-interaction

# Set directory permissions
RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache

# Expose port 80 and boot supervisor
EXPOSE 80
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
Enter fullscreen mode Exit fullscreen mode

2. Installation and Initializing Kamal

Ensure you have Docker running locally. Then, install Kamal on your local development machine:

gem install kamal
Enter fullscreen mode Exit fullscreen mode

Once installed, run the initialization command in your Laravel project root:

kamal init
Enter fullscreen mode Exit fullscreen mode

This command generates three key files:

  • config/deploy.yml: The primary deployment configuration file.
  • .env: Locally contains secret keys and registry passwords (ignored by git).
  • .kamal/secrets: Scripts for managing secrets securely.

3. Configuring config/deploy.yml

The deploy.yml file defines your servers, your container image registry, and your service configurations. Here is a production configuration setting up a Laravel app and a Redis server on a single VPS:

service: my-laravel-app

image: klytron/my-laravel-app

servers:
  web:
    hosts:
      - 192.0.2.1 # Your VPS IP address
    labels:
      traefik.http.routers.my-laravel-app.rule: "Host(`app.example.com`)"

registry:
  username: klytron
  password:
    - KAMAL_REGISTRY_PASSWORD

env:
  clear:
    DB_CONNECTION: pgsql
    DB_HOST: 192.0.2.1
    DB_PORT: 5432
    DB_DATABASE: my_database
    REDIS_HOST: my-laravel-app-redis
  secret:
    - APP_KEY
    - DB_PASSWORD

accessories:
  redis:
    image: redis:7.2-alpine
    roles:
      - web
    port: "6379:6379"
Enter fullscreen mode Exit fullscreen mode

4. Managing Environment Secrets

To prevent sensitive API keys or database passwords from leaking, Kamal retrieves secrets from your local .env file on deployment and injects them into the running container securely.

Define your local variables in your local .env:

KAMAL_REGISTRY_PASSWORD=dckr_pat_your_docker_hub_token
APP_KEY=base64:your_production_laravel_key
DB_PASSWORD=your_production_database_password
Enter fullscreen mode Exit fullscreen mode

When you deploy, Kamal reads these values and writes them to an encrypted environment file inside the target server.

5. Executing the First Deployment

With your servers, registry, and environment variables configured, run the setup routine:

```bash\ kamal setup




This command will:
1. SSH into your target server.
2. Install Docker (if not present).
3. Install Traefik as a reverse proxy.
4. Boot configured accessories like Redis.
5. Build, push, and pull your Docker images.
6. Start containers and manage traffic routing.

For subsequent updates, run `kamal deploy` for seamless zero-downtime rolling updates.

šŸ‘‰ **[Read the complete deep-dive with the full code repository and bonus security checklist on klytron.com](https://klytron.com/blog/moving-off-paas-kamal-2-laravel)**
Enter fullscreen mode Exit fullscreen mode

Top comments (0)