How To Bootstrap a New Laravel Application with Docker Compose

To get started, you’ll need to create a containerized environment able to execute PHP and Composer, the PHP dependency management tool. Then, you’ll be able to bootstrap the new Laravel application from scratch, without the need to have a local PHP environment installed on your local machine or development server.

In this guide, we’ll provide streamlined instructions on how to set this environment up based on our tutorial on How To Install Laravel with Docker Compose on Ubuntu 20.04. Please refer to that tutorial for more detailed instructions on each of the options used within the Docker Compose file that will be provided in this guide.

Create a new directory for your application in your home folder:

  • mkdir ~/landing-laravel
  • cd ~/landing-laravel

Next, you’ll create the docker-compose.yml file that will define the containerized environment. In this file, you’ll set up a service named app, which will be based on a custom Docker image built with a Dockerfile you’ll set up later on.

The build arguments user and uid, both defined in the docker-compose.yml file and used in the Dockerfile at build time, should be changed to reflect your own username and uid on your local machine or development server. To find out your current user’s uid, type:

  • echo $UID
Output1000 

The user and uid variables will be available at build time and will be used in the Dockerfile to create a new user in the app service with the same username and uid as your current system user on your local machine or development server. This will avoid permission and ownership issues when working with application files both from the container as well as from the host that executes Docker.

Create a new docker-compose.yml file using your text editor of choice. Here, we’re using nano:

  • nano docker-compose.yml

Copy the following content to this file, and don’t forget to replace the highlighted values with appropriate values depending on your own username and uid on the system that runs Docker:

~/landing-laravel/docker-compose.yml

version: "3.7" services:   app:     build:       args:         user: sammy         uid: 1000       context: ./       dockerfile: Dockerfile     image: landing-app     restart: unless-stopped     working_dir: /var/www/     volumes:       - ./:/var/www     networks:       - landing  networks:   landing:     driver: bridge 

Save and close the file when you are done. If you are using nano, you can do that by pressing CTRL+X, then Y and ENTER to confirm.

Next, you’ll set up the Dockerfile that is referenced in the docker-compose.yml file, which will set up a custom image for the app service:

  • nano Dockerfile

This Dockerfile extends from the default php:7.4-fpm Docker image. It uses the user and uid variables to create a new user able to execute Artisan and Composer commands. It also installs a few PHP dependencies that are required by Laravel, and the Composer executable.

Copy the following content to your Dockerfile:

~/my-todo-list/Dockerfile

FROM php:7.4-fpm  # Arguments defined in docker-compose.yml ARG user ARG uid  # Install system dependencies RUN apt-get update && apt-get install -y      git      curl      libpng-dev      libonig-dev      libxml2-dev      zip      unzip  # Clear cache RUN apt-get clean && rm -rf /var/lib/apt/lists/*  # Install PHP extensions RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd  # Get latest Composer COPY --from=composer:latest /usr/bin/composer /usr/bin/composer  # Create system user to run Composer and Artisan Commands RUN useradd -G www-data,root -u $uid -d /home/$user $user RUN mkdir -p /home/$user/.composer &&      chown -R $user:$user /home/$user  # Set working directory WORKDIR /var/www  USER $user 

Save and close the file when you’re done. Next, you can bring your environment up with:

  • docker-compose up -d

This command will execute Docker Compose in detached mode, which means it will run in the background. The first time you bring an environment up with a custom image, Docker Compose will automatically build the image for you before creating the required containers. This might take a few moments to finish. You’ll see output similar to this:

OutputCreating network "landing-laravel_landing" with driver "bridge" Building app Step 1/11 : FROM php:7.4-fpm  ---> fa37bd6db22a ... Step 10/11 : WORKDIR /var/www  ---> Using cache  ---> 769afd5d44d8 Step 11/11 : USER $user  ---> Using cache  ---> 841eb5852b69  Successfully built 841eb5852b69 Successfully tagged landing-app:latest WARNING: Image for service app was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`. Creating landing-laravel_app_1 ... done  

You can verify that your environment is up and running with:

  • docker-compose ps
Output        Name                       Command              State    Ports   ------------------------------------------------------------------------ landing-laravel_app_1   docker-php-entrypoint php-fpm   Up      9000/tcp 

Once the app service is up, you can run Composer, the PHP dependency management tool, to bootstrap a new Laravel application. In order to do that, you’ll use docker compose exec to run commands on the app service, where PHP is installed.

The following command will use Docker Compose to execute composer create-project, which will bootstrap a fresh installation of Laravel based on the laravel/laravel package:

  • docker-compose exec app composer create-project laravel/laravel --prefer-dist application
Creating a "laravel/laravel" project at "./application" Installing laravel/laravel (v8.4.0)   - Downloading laravel/laravel (v8.4.0)   - Installing laravel/laravel (v8.4.0): Extracting archive Created project in /var/www/application > @php -r "file_exists('.env') || copy('.env.example', '.env');" Loading composer repositories with package information Updating dependencies Lock file operations: 104 installs, 0 updates, 0 removals … Package manifest generated successfully. 71 packages you are using are looking for funding. Use the `composer fund` command to find out more! > @php artisan key:generate --ansi Application key set successfully. 

This installation creates a new .env file based on the default .env.example file that comes with Laravel. The .env file contains database credentials and other sensitive application settings, and should be unique per environment where the app runs. You’ll come back to edit this file after you finish setting up the development environment.

Next, copy the application files to the same directory as the docker-compose.yml file, so that you can share Laravel’s environment variables file with Docker Compose. Then, you can remove the application directory created by Composer:

cp -rT application . rm -rfv application 

Your application is now bootstrapped, but you’ll need to include a couple services in the Docker Compose file in order to be able to access the app from a browser. An nginx service will serve the application using the Nginx web server, and a db service will host the application’s MySQL database.

First, bring your environment down with:

  • docker-compose down
OutputStopping landing-laravel_app_1 ... done Removing landing-laravel_app_1 ... done Removing network landing-laravel_landing 

This will remove all containers and networks associated with this environment. Before editing your docker-compose.yml file to add the new services, create a new directory to share configuration files with containers. You’ll need this to properly set up Nginx to handle the Laravel PHP application.

  • mkdir -p docker-compose/nginx

Next, create a new landing-laravel.conf file containing a custom Nginx server block. Later on, you’ll set up a volume to share this file within the nginx service container.

Open a new Nginx configuration file with:

  • nano docker-compose/nginx/landing-laravel.conf

The following server block configures Nginx to serve a Laravel application using an external service (app) to handle PHP code. Copy this content to your own Nginx configuration file:

docker-compose/nginx/landing-laravel.conf

server {     listen 80;     index index.php index.html;     error_log  /var/log/nginx/error.log;     access_log /var/log/nginx/access.log;     root /var/www/public;     location ~ .php$ {         try_files $uri =404;         fastcgi_split_path_info ^(.+.php)(/.+)$;         fastcgi_pass app:9000;         fastcgi_index index.php;         include fastcgi_params;         fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;         fastcgi_param PATH_INFO $fastcgi_path_info;     }     location / {         try_files $uri $uri/ /index.php?$query_string;         gzip_static on;     } } 

Save and close the file when you’re done.

Next, open your docker-compose.yml file:

  • nano docker-compose.yml

Include the following configuration for the nginx service, at the same level as the previously configured app service. This will create a new service based on the nginx:alpine image, and all requests on port 8000 of the host where Docker is running will be redirected to port 80 in the service container. In addition to the application files, you’ll also share a volume containing Nginx’s configuration file for a Laravel application:

  nginx:     image: nginx:alpine     restart: unless-stopped     ports:       - 8000:80     volumes:       - ./:/var/www       - ./docker-compose/nginx:/etc/nginx/conf.d/     networks:       - landing  

Then, include the following configuration block for the db service. This will create a service based on the default MySQL 8 image, and pull in the values defined in Laravel’s environment file to set up database access:

  db:     image: mysql:8     restart: unless-stopped     environment:       MYSQL_DATABASE: ${DB_DATABASE}       MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}       MYSQL_PASSWORD: ${DB_PASSWORD}       MYSQL_USER: ${DB_USERNAME}     networks:       - landing 

This is how your updated docker-compose.yml file should look like once you’re finished:

~/landing-laravel/docker-compose.yml

version: "3.7" services:   app:     build:       args:         user: sammy         uid: 1000       context: ./       dockerfile: Dockerfile     image: landing-app     restart: unless-stopped     working_dir: /var/www/     volumes:       - ./:/var/www     networks:       - landing    nginx:     image: nginx:alpine     restart: unless-stopped     ports:       - 8000:80     volumes:       - ./:/var/www       - ./docker-compose/nginx:/etc/nginx/conf.d/     networks:       - landing   db:     image: mysql:8     restart: unless-stopped     environment:       MYSQL_DATABASE: ${DB_DATABASE}       MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}       MYSQL_PASSWORD: ${DB_PASSWORD}       MYSQL_USER: ${DB_USERNAME}     networks:       - landing  networks:   landing:     driver: bridge  

Note: for more detailed information about containerizing Laravel environments, including explanations about shared volumes and networks, please refer to our full guide on How To Install Laravel with Docker Compose on Ubuntu 20.04.

Save and close the file when you’re done editing. Lastly, update your Laravel dot env file (.env) to point the MySQL database host configuration to the host where the MySQL service will be running, called db:

  • nano .env

The .env file that is automatically generated by Composer upon installation comes with some default values that you might want to change, such as the APP_NAME and the APP_URL. The database DB_HOST variable must be changed to point to the service where MySQL will be running, and you can reference it by its service name, as defined in the docker-compose.yml file. In this example, we’ve used db as name for the database service, so this will be available in the containerized network as a host named db.

Change your .env accordingly, using the following example as base. The highlighted values were updated here to reflect the state of the application under development:

~/landing-laravel/.env

APP_NAME=LandingLaravel APP_ENV=local APP_KEY=base64:ffYPNP8kPeQDf8gE/qh3kWjk59p6gFY66kCKhhKUa2w= APP_DEBUG=true APP_URL=http://localhost:8000  LOG_CHANNEL=stack LOG_LEVEL=debug  DB_CONNECTION=mysql DB_HOST=db DB_PORT=3306 DB_DATABASE=landing-db DB_USERNAME=landing-user DB_PASSWORD=dev-password  ... 

You don’t need to change any other sections of this file, but feel free to tweak to your specific use case.

Save and close the file when you’re done editing its contents.

You can now bring the updated environment up with:

  • docker-compose up -d
OutputCreating network "landing-laravel_landing" with driver "bridge" Creating landing-laravel_app_1   ... done Creating landing-laravel_db_1    ... done Creating landing-laravel_nginx_1 ... done 

With the full environment up, you can now point your browser to localhost or your remote server’s IP address, on port 8000:

http://localhost:8000 

If everything works as expected, you’ll see a page like this:

Laravel Landing Links - basic app

In the next part of this series, you’ll create a database migration to set up a links table.