├── php ├── php.ini └── opcache.ini ├── scripts ├── app-entrypoint.sh └── backup-mysql-to-s3.sh ├── .dockerignore ├── README.md ├── backup-mysql-to-s3.dockerfile ├── queue.dockerfile ├── app.dockerfile └── scheduler.dockerfile /php/php.ini: -------------------------------------------------------------------------------- 1 | [PHP] 2 | post_max_size = 100M 3 | upload_max_filesize = 100M 4 | expose_php = 0 5 | variables_order = "GPCS" 6 | -------------------------------------------------------------------------------- /php/opcache.ini: -------------------------------------------------------------------------------- 1 | [Opcache] 2 | opcache.enable = 1 3 | opcache.enable_cli = 1 4 | opcache.memory_consumption = 256M 5 | opcache.use_cwd = 0 6 | opcache.max_file_size = 0 7 | opcache.max_accelerated_files = 32531 8 | opcache.validate_timestamps = 0 9 | opcache.revalidate_freq = 0 10 | 11 | [JIT] 12 | opcache.jit_buffer_size = 100M 13 | opcache.jit = function 14 | -------------------------------------------------------------------------------- /scripts/app-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | initialStuff() { 5 | php artisan optimize:clear 6 | php artisan package:discover --ansi 7 | php artisan config:cache 8 | php artisan route:cache 9 | php artisan view:cache 10 | php artisan event:cache 11 | } 12 | 13 | initialStuff 14 | 15 | php artisan octane:start --server=swoole --host=0.0.0.0 --port=9000 --workers=auto --task-workers=auto --max-requests=500 16 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .github 3 | 4 | .idea 5 | .vscode 6 | 7 | bootstrap/cache/*.php 8 | 9 | storage/app/* 10 | storage/debugbar/* 11 | storage/framework/* 12 | storage/logs/* 13 | 14 | node_modules 15 | public/build 16 | tests 17 | vendor 18 | .editorconfig 19 | .env 20 | .env.example 21 | .gitattributes 22 | .gitignore 23 | .phpunit.result.cache 24 | _ide_helper.php 25 | _ide_helper_models.php 26 | CHANGELOG.md 27 | docker-compose.yml 28 | phpunit.xml 29 | pint.json 30 | README.md 31 | 32 | .DS_Store 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel Container 2 | 3 | Containerizing your Laravel App with [Docker](https://www.docker.com). 4 | 5 | ## How to Use 6 | 7 | Clone this project in your Laravel APP. 8 | 9 | ```bash 10 | cd your-laravel-app 11 | 12 | git clone https://github.com/YilanBoy/laravel-container.git 13 | 14 | cp laravel-container/.dockerignore ./.dockerignore 15 | ``` 16 | 17 | Then you can start to build container. 18 | 19 | ```bash 20 | # build app 21 | docker buildx build -f laravel-container/app.dockerfile --platform linux/amd64,linux/arm64 --push -t laravel-app:latest . 22 | 23 | # build queue 24 | docker buildx build -f laravel-container/queue.dockerfile --platform linux/amd64,linux/arm64 --push -t laravel-queue:latest . 25 | 26 | # build scheduler 27 | docker buildx build -f laravel-container/scheduler.dockerfile --platform linux/amd64,linux/arm64 --push -t laravel-scheduler:latest . 28 | ``` 29 | -------------------------------------------------------------------------------- /backup-mysql-to-s3.dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | SHELL ["/bin/bash", "-exou", "pipefail", "-c"] 4 | 5 | RUN apt update \ 6 | && apt upgrade -y \ 7 | && apt install -y \ 8 | curl \ 9 | zip \ 10 | unzip \ 11 | mysql-client 12 | 13 | RUN if [ "$(uname -m)" = "x86_64" ]; then \ 14 | curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" \ 15 | && unzip awscliv2.zip \ 16 | && ./aws/install; \ 17 | elif [ "$(uname -m)" = "aarch64" ]; then \ 18 | curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip" \ 19 | && unzip awscliv2.zip \ 20 | && ./aws/install; \ 21 | else \ 22 | echo "Unsupported platform" && exit 1; \ 23 | fi 24 | 25 | ARG WWWUSER=1001 26 | ARG WWWGROUP=1001 27 | 28 | # create group and user "scheduler" 29 | RUN groupadd -g $WWWGROUP scheduler || true \ 30 | && useradd -ms /bin/bash --no-log-init --no-user-group -g $WWWGROUP -u $WWWUSER scheduler 31 | 32 | # set the working directory 33 | WORKDIR /home/scheduler 34 | 35 | # copy the entrypoint script 36 | COPY containerize/scripts/aws-mysql-client.sh aws-mysql-client.sh 37 | 38 | # set scripts to start the laravel octane app 39 | RUN chmod +x aws-mysql-client.sh 40 | 41 | # set the user 42 | USER scheduler 43 | 44 | ENTRYPOINT ["scripts/backup-mysql-to-s3.sh"] 45 | 46 | CMD ["/bin/bash"] 47 | -------------------------------------------------------------------------------- /scripts/backup-mysql-to-s3.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | # Check if the environment variables "DATABASE_HOST" are set 6 | if [ -z "$DATABASE_HOST" ]; then 7 | echo "The environment variable DATABASE_HOST is not set. Exiting..." 8 | exit 1 9 | fi 10 | 11 | # Check if the environment variables "DATABASE_PORT" are set 12 | if [ -z "$DATABASE_PORT" ]; then 13 | echo "The environment variable DATABASE_PORT is not set. Exiting..." 14 | exit 1 15 | fi 16 | 17 | # Check if the environment variables "DATABASE_USER" are set 18 | if [ -z "$DATABASE_USER" ]; then 19 | echo "The environment variable DATABASE_USER is not set. Exiting..." 20 | exit 1 21 | fi 22 | 23 | # Check if the environment variables "DATABASE_PASSWORD" are set 24 | if [ -z "$DATABASE_PASSWORD" ]; then 25 | echo "The environment variable DATABASE_PASSWORD is not set. Exiting..." 26 | exit 1 27 | fi 28 | 29 | # Check if the environment variables "DATABASE_NAME" are set 30 | if [ -z "$DATABASE_NAME" ]; then 31 | echo "The environment variable DATABASE_NAME is not set. Exiting..." 32 | exit 1 33 | fi 34 | 35 | # Check if the environment variables "BACKUP_FILE_NAME" are set 36 | if [ -z "$BACKUP_FILE_NAME" ]; then 37 | echo "The environment variable BACKUP_FILE_NAME is not set. Exiting..." 38 | exit 1 39 | fi 40 | 41 | # Check if the environment variables "S3_BUCKET_NAME" are set 42 | if [ -z "$S3_BUCKET_NAME" ]; then 43 | echo "The environment variable S3_BUCKET_NAME is not set. Exiting..." 44 | exit 1 45 | fi 46 | 47 | # Check if the "AWS_ACCESS_KEY_ID" environment variable is set 48 | if [ -z "$AWS_ACCESS_KEY_ID" ]; then 49 | echo "The environment variable AWS_ACCESS_KEY_ID is not set. Exiting..." 50 | exit 1 51 | fi 52 | 53 | # Check if the "AWS_SECRET_ACCESS_KEY" environment variable is set 54 | if [ -z "$AWS_SECRET_ACCESS_KEY" ]; then 55 | echo "The environment variable AWS_SECRET_ACCESS_KEY is not set. Exiting..." 56 | exit 1 57 | fi 58 | 59 | # Check if the "AWS_DEFAULT_REGION" environment variable is set 60 | if [ -z "$AWS_DEFAULT_REGION" ]; then 61 | echo "The environment variable AWS_DEFAULT_REGION is not set. Exiting..." 62 | exit 1 63 | fi 64 | 65 | # Check the mysqldump command is available 66 | if ! command -v mysqldump &>/dev/null; then 67 | echo "The mysqldump command is not available. Exiting..." 68 | exit 1 69 | fi 70 | 71 | # Check the aws command is available 72 | if ! command -v aws &>/dev/null; then 73 | echo "The aws command is not available. Exiting..." 74 | exit 1 75 | fi 76 | -------------------------------------------------------------------------------- /queue.dockerfile: -------------------------------------------------------------------------------- 1 | ARG PHP_VERSION=8.3 2 | 3 | ########################################### 4 | # Laravel Dependencies 5 | ########################################### 6 | FROM composer:latest AS vendor 7 | 8 | WORKDIR /var/www/html 9 | 10 | # copy only the 'composer.json' and 'composer.lock' files into the container 11 | COPY composer* ./ 12 | RUN composer install \ 13 | --no-dev \ 14 | --no-interaction \ 15 | --prefer-dist \ 16 | --ignore-platform-reqs \ 17 | --optimize-autoloader \ 18 | --apcu-autoloader \ 19 | --ansi \ 20 | --no-scripts \ 21 | --audit 22 | 23 | ########################################### 24 | # Front-End Assets 25 | ########################################### 26 | FROM node:22 AS assets 27 | 28 | WORKDIR /var/www/html 29 | 30 | COPY . . 31 | RUN npm install \ 32 | && npm run build 33 | 34 | ########################################### 35 | # Laravel Queue 36 | ########################################### 37 | FROM php:${PHP_VERSION}-alpine3.20 38 | 39 | LABEL maintainer="Allen" 40 | 41 | ENV ROOT=/var/www/html 42 | WORKDIR $ROOT 43 | 44 | # set the default shell to /bin/ash with some useful options 45 | # -e: exit immediately if a command exits with a non-zero status 46 | # -c: execute the following command when the shell starts 47 | SHELL ["/bin/ash", "-e", "-c"] 48 | 49 | # install necessary package to install php extension 50 | RUN apk update \ 51 | && apk upgrade \ 52 | && apk add autoconf gcc g++ make 53 | 54 | # install php extension 55 | RUN docker-php-ext-install pdo_mysql \ 56 | && docker-php-ext-install opcache \ 57 | && docker-php-ext-install pcntl \ 58 | && pecl install redis \ 59 | && docker-php-ext-enable redis 60 | 61 | ARG WWWUSER=1001 62 | ARG WWWGROUP=1001 63 | 64 | # create group and user "queue" 65 | RUN addgroup -g $WWWGROUP -S queue || true \ 66 | && adduser -D -h /home/queue -s /bin/ash -G queue -u $WWWUSER queue 67 | 68 | # copy supervisor and php config files into container 69 | COPY laravel-container/php/php.ini /usr/local/etc/php/conf.d/queue.ini 70 | COPY laravel-container/php/opcache.ini /usr/local/etc/php/conf.d/opcache.ini 71 | 72 | COPY . . 73 | 74 | # create bootstrap and storage files if they do not exist 75 | # gives the 'queue' user read/write and execute privileges to those files 76 | RUN mkdir -p \ 77 | storage/framework/sessions \ 78 | storage/framework/views \ 79 | storage/framework/cache/data \ 80 | storage/logs \ 81 | bootstrap/cache \ 82 | && chown -R queue:queue \ 83 | storage \ 84 | bootstrap/cache \ 85 | && chmod -R ug+rwx storage bootstrap/cache 86 | 87 | # copy dependencies from another stage 88 | COPY --from=vendor ${ROOT}/vendor vendor 89 | COPY --from=assets ${ROOT}/public/build public/build 90 | 91 | USER queue 92 | 93 | ENTRYPOINT ["php", "artisan", "queue:work"] 94 | -------------------------------------------------------------------------------- /app.dockerfile: -------------------------------------------------------------------------------- 1 | ########################################### 2 | # Laravel Dependencies 3 | ########################################### 4 | FROM composer:latest AS vendor 5 | 6 | WORKDIR /var/www/html 7 | 8 | # copy only the 'composer.json' and 'composer.lock' files into the container 9 | COPY composer* ./ 10 | RUN composer install \ 11 | --no-dev \ 12 | --no-interaction \ 13 | --prefer-dist \ 14 | --ignore-platform-reqs \ 15 | --optimize-autoloader \ 16 | --apcu-autoloader \ 17 | --ansi \ 18 | --no-scripts \ 19 | --audit 20 | 21 | ########################################### 22 | # Front-End Assets 23 | ########################################### 24 | FROM node:22 AS assets 25 | 26 | WORKDIR /var/www/html 27 | 28 | COPY . . 29 | RUN npm install \ 30 | && npm run build 31 | 32 | ########################################### 33 | # Laravel Octane 34 | ########################################### 35 | FROM ubuntu:24.04 36 | 37 | LABEL maintainer="Allen" 38 | 39 | ENV ROOT=/var/www/html 40 | ENV TZ=Asia/Taipei 41 | WORKDIR $ROOT 42 | 43 | # set the default shell to /bin/bash with some useful options 44 | # -e: exit immediately if a command exits with a non-zero status 45 | # -x: print each command before executing it 46 | # -o pipefail: fail if any command in a pipeline fails 47 | # -u: treat unset variables as an error when substituting 48 | SHELL ["/bin/bash", "-exou", "pipefail", "-c"] 49 | 50 | # install necessary package to install php extension 51 | RUN apt-get update \ 52 | && apt-get upgrade -y \ 53 | && apt-get install software-properties-common -y 54 | 55 | # install php and php extension 56 | RUN add-apt-repository ppa:ondrej/php -y \ 57 | && apt-get update \ 58 | && DEBIAN_FRONTEND=noninteractive apt-get install php8.3 php8.3-{cli,common,curl,xml,mbstring,redis,swoole} -y 59 | 60 | ARG WWWUSER=1001 61 | ARG WWWGROUP=1001 62 | 63 | # create group and user "octane" 64 | RUN groupadd --force -g $WWWGROUP octane \ 65 | && useradd -ms /bin/bash --no-log-init --no-user-group -g $WWWGROUP -u $WWWUSER octane 66 | 67 | # copy php config files into container 68 | COPY laravel-container/php/php.ini /usr/local/etc/php/conf.d/octane.ini 69 | COPY laravel-container/php/opcache.ini /usr/local/etc/php/conf.d/opcache.ini 70 | 71 | COPY . . 72 | 73 | # set scripts to start the laravel octane app 74 | RUN chmod +x laravel-container/scripts/app-entrypoint.sh 75 | 76 | # create bootstrap and storage files if they do not exist 77 | # gives the 'octane' user read/write and execute privileges to those files 78 | RUN mkdir -p \ 79 | storage/framework/sessions \ 80 | storage/framework/views \ 81 | storage/framework/cache/data \ 82 | storage/logs \ 83 | bootstrap/cache \ 84 | && chown -R octane:octane \ 85 | storage \ 86 | bootstrap/cache \ 87 | && chmod -R ug+rwx storage bootstrap/cache 88 | 89 | # copy dependencies from another stage 90 | COPY --from=vendor ${ROOT}/vendor vendor 91 | COPY --from=assets ${ROOT}/public/build public/build 92 | 93 | EXPOSE 9000 94 | 95 | USER octane 96 | 97 | ENTRYPOINT ["laravel-container/scripts/app-entrypoint.sh"] 98 | 99 | HEALTHCHECK --start-period=5s --interval=30s --timeout=5s --retries=8 \ 100 | CMD curl --fail localhost:9000/up || exit 1 101 | -------------------------------------------------------------------------------- /scheduler.dockerfile: -------------------------------------------------------------------------------- 1 | ARG PHP_VERSION=8.3 2 | ARG SUPERCRONIC_VERSION=0.2.33 3 | 4 | ########################################### 5 | # PHP Dependencies 6 | ########################################### 7 | FROM composer:latest AS vendor 8 | 9 | WORKDIR /var/www/html 10 | 11 | # copy only the 'composer.json' and 'composer.lock' files into the container 12 | COPY composer* ./ 13 | RUN composer install \ 14 | --no-dev \ 15 | --no-interaction \ 16 | --prefer-dist \ 17 | --ignore-platform-reqs \ 18 | --optimize-autoloader \ 19 | --apcu-autoloader \ 20 | --ansi \ 21 | --no-scripts \ 22 | --audit 23 | 24 | ########################################### 25 | # Scheduler 26 | ########################################### 27 | FROM php:${PHP_VERSION}-alpine3.20 28 | ARG SUPERCRONIC_VERSION 29 | 30 | LABEL maintainer="Allen" 31 | 32 | ENV ROOT=/var/www/html 33 | WORKDIR $ROOT 34 | 35 | # set the default shell to /bin/ash with some useful options 36 | # -e: exit immediately if a command exits with a non-zero status 37 | # -c: execute the following command when the shell starts 38 | SHELL ["/bin/ash", "-e", "-c"] 39 | 40 | # install necessary package to install php extension 41 | RUN apk update \ 42 | && apk upgrade \ 43 | && apk add autoconf gcc g++ make 44 | 45 | # install php extension 46 | RUN docker-php-ext-install pdo_mysql \ 47 | && docker-php-ext-install opcache \ 48 | && docker-php-ext-install pcntl \ 49 | && pecl install redis \ 50 | && docker-php-ext-enable redis 51 | 52 | ARG WWWUSER=1001 53 | ARG WWWGROUP=1001 54 | 55 | # create group and user "scheduler" 56 | RUN addgroup -g $WWWGROUP -S scheduler || true \ 57 | && adduser -D -h /home/scheduler -s /bin/ash -G scheduler -u $WWWUSER scheduler 58 | 59 | # copy supervisor and php config files into container 60 | COPY laravel-container/php/php.ini /usr/local/etc/php/conf.d/scheduler.ini 61 | COPY laravel-container/php/opcache.ini /usr/local/etc/php/conf.d/opcache.ini 62 | 63 | # set supercronic for schedules 64 | # download the corresponding files according to the different architectures. 65 | RUN if [ "$(uname -m)" = "x86_64" ]; then \ 66 | wget -q "https://github.com/aptible/supercronic/releases/download/v${SUPERCRONIC_VERSION}/supercronic-linux-amd64" \ 67 | -O /usr/bin/supercronic; \ 68 | elif [ "$(uname -m)" = "aarch64" ]; then \ 69 | wget -q "https://github.com/aptible/supercronic/releases/download/v${SUPERCRONIC_VERSION}/supercronic-linux-arm64" \ 70 | -O /usr/bin/supercronic; \ 71 | else \ 72 | echo "Unsupported platform" && exit 1; \ 73 | fi \ 74 | && chmod +x /usr/bin/supercronic \ 75 | && mkdir -p /etc/supercronic \ 76 | && echo "*/1 * * * * php ${ROOT}/artisan schedule:run --verbose --no-interaction" > /etc/supercronic/laravel 77 | 78 | COPY . . 79 | 80 | # create bootstrap and storage files if they do not exist 81 | # gives the 'scheduler' user read/write and execute privileges to those files 82 | RUN mkdir -p \ 83 | storage/framework/sessions \ 84 | storage/framework/views \ 85 | storage/framework/cache/data \ 86 | storage/logs \ 87 | bootstrap/cache \ 88 | && chown -R scheduler:scheduler \ 89 | storage \ 90 | bootstrap/cache \ 91 | && chmod -R ug+rwx storage bootstrap/cache 92 | 93 | # copy dependencies from another stage 94 | COPY --from=vendor ${ROOT}/vendor vendor 95 | 96 | HEALTHCHECK --start-period=5s --interval=30s --timeout=3s \ 97 | CMD supercronic -test /etc/supercronic/laravel 98 | 99 | USER scheduler 100 | 101 | ENTRYPOINT ["supercronic", "/etc/supercronic/laravel"] 102 | --------------------------------------------------------------------------------