Laravel with NGINX Unit
Tags: laravel, nginx, php, docker
Published: 2023-12-13 20:44:10

In this brief tutorial, I'll provide a quick overview of how I hosted a small Laravel application using the new lightweight Nginx Unit Server.

## Why?

My goal was to host a Laravel application in Docker without the overhead of managing nginx and PHP-FPM. While I could use the built-in command `php artisan serve`, it's not as performant as PHP-FPM. Therefore, I decided to utilize the new [Unit](https://unit.nginx.org/) Server from Nginx.

## How?

### The configuration file

Unit is configured via a handy JSON file. This file allows me to define the type of application to be executed and the location of static files.
```js
{
    "listeners": {
        "*:80": {
            "pass": "routes"
        }
    },
    "routes": [
        {
            "match": {
                "uri": "!/index.php"
            },
            "action": {
                "share": "/var/www/html/public$uri",
                "response_headers": {
                    "Cache-Control": "max-age=60, s-maxage=120"
                },
                "fallback": {
                    "pass": "applications/laravel"
                }
            }
        }
    ],
    "applications": {
        "laravel": {
            "type": "php",
            "root": "/var/www/html/public/",
            "script": "index.php"
        }
    }
}

```
1. Initially, I defined a listener on a specific port.
2. This listener directs all requests, checking if the requested asset is static; otherwise, it falls back to the application block.
3. In the application config, I defined the language to be used and the script's location for execution.

### Dockerfile

Fortunately, Unit provides prebuilt Docker images that I utilized in the following steps.
```Dockerfile
FROM unit:php8.2

# Prepare workfir
RUN mkdir -p /var/www/html
RUN mkdir -p /var/www/html/databasestore
WORKDIR /var/www/html

# Pullin build artifacts from build stage
COPY --from=build --chown=unit:unit /var/www/html /var/www/html

# Set timezone
ENV TZ=Europe/Berlin

# Update package lists and install required dependencies
RUN apt-get update && apt-get install -y \
    libicu-dev \
    libjpeg-dev \
    libpng-dev \
    libfreetype6-dev \
    sudo \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Install and enable the intl and gd extensions
RUN docker-php-ext-install intl gd

COPY docker/config.json /docker-entrypoint.d/config.json

COPY docker/entrypoint.sh /docker-entrypoint.d/entrypoint.sh

RUN chmod +x /docker-entrypoint.d/entrypoint.sh

EXPOSE 80
```
1. 1. Initially, I set up everything, including creating the workspace, setting the timezone, and installing required extensions.
2. The most crucial tasks are the last two `COPY` commands, placing `config.json` and `entrypoint.sh` in `/docker-entrypoint.d`. Unit will reconfigure itself using `config.json` and then execute `entrypoint.sh`, which migrates the database, flushes the config, etc.

Now, I have a relatively compact image that serves my Laravel application much more efficiently than the built-in serve command. It's easier to configure and has less overhead.

You can find an example repository here: [https://github.com/chaostreff-flensburg/simple-borrow](https://github.com/chaostreff-flensburg/simple-borrow)