├── 8.x
├── shared
│ ├── crontab
│ ├── start-server.sh
│ ├── php.ini
│ └── entrypoint.sh
├── Dockerfile.octane.swoole
├── Dockerfile.octane.openswoole
├── Dockerfile.octane.roadrunner
├── .dockerignore
├── Dockerfile.octane.frankenphp
└── Dockerfile
├── .idea
├── vcs.xml
├── .gitignore
├── laravel-alpine-images.iml
├── modules.xml
└── php.xml
├── .github
├── actions
│ ├── generate-image-tags
│ │ ├── action.yml
│ │ └── generate-image-tags.php
│ ├── version-outdated
│ │ └── action.yml
│ └── runtime-version-compare
│ │ └── action.yml
└── workflows
│ └── main.yml
├── LICENSE
├── .gitignore
└── README.md
/8.x/shared/crontab:
--------------------------------------------------------------------------------
1 | * * * * * su-exec laravel php $APP_DIRECTORY/artisan schedule:run > /proc/1/fd/1 2>&1
2 |
--------------------------------------------------------------------------------
/8.x/shared/start-server.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | php -d variables_order=EGPCS $APP_DIRECTORY/artisan serve --host=0.0.0.0 --port=80
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/.idea/laravel-alpine-images.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/8.x/Dockerfile.octane.swoole:
--------------------------------------------------------------------------------
1 | ARG PHP_VERSION
2 | FROM ghcr.io/iksaku/laravel-alpine:${PHP_VERSION}-cli
3 |
4 | # Overwrite entrypoint to use Octane
5 | RUN sed -i \
6 | 's/artisan serve/artisan octane:start --server=swoole/' \
7 | /usr/local/bin/start-server
8 |
9 | # Install PHP extensions required by Octane
10 | RUN install-php-extensions swoole
11 |
--------------------------------------------------------------------------------
/8.x/Dockerfile.octane.openswoole:
--------------------------------------------------------------------------------
1 | ARG PHP_VERSION
2 | FROM ghcr.io/iksaku/laravel-alpine:${PHP_VERSION}-cli
3 |
4 | # Overwrite entrypoint to use Octane
5 | RUN sed -i \
6 | 's/artisan serve/artisan octane:start --server=swoole/' \
7 | /usr/local/bin/start-server
8 |
9 | # Install PHP extensions required by Octane
10 | RUN install-php-extensions openswoole
11 |
--------------------------------------------------------------------------------
/8.x/Dockerfile.octane.roadrunner:
--------------------------------------------------------------------------------
1 | ARG PHP_VERSION
2 | FROM ghcr.io/iksaku/laravel-alpine:${PHP_VERSION}-cli
3 |
4 | # Overwrite entrypoint to use Octane
5 | RUN sed -i \
6 | 's/artisan serve/artisan octane:start --server=roadrunner --rpc-port=6001/' \
7 | /usr/local/bin/start-server
8 |
9 | # Install PHP extensions required by Octane
10 | RUN install-php-extensions sockets
11 |
12 | # Install RoadRunner binary
13 | COPY --from=ghcr.io/roadrunner-server/roadrunner:latest /usr/bin/rr /usr/local/bin/rr
14 |
15 | RUN setcap "cap_net_bind_service=+ep" /usr/local/bin/rr
--------------------------------------------------------------------------------
/8.x/.dockerignore:
--------------------------------------------------------------------------------
1 | # Based of @fideloper's Fly.io template
2 | # https://github.com/superfly/flyctl/blob/698f3bda500015354773a2544c90f0b2a6b5ca3a/scanner/templates/laravel/.dockerignore
3 |
4 | # 1. Ignore Laravel-specific files we don't need
5 | bootstrap/cache/*
6 | storage/framework/cache/*
7 | storage/framework/sessions/*
8 | storage/framework/views/*
9 | storage/logs/*
10 | *.env*
11 | .rr.yaml
12 | rr
13 |
14 | # 2. Ignore common files/directories we don't need
15 | .vscode
16 | .idea
17 | vendor
18 | **/*node_modules
19 | **.git
20 | **.gitignore
21 | **.gitattributes
22 | **.sass-cache
23 | **/*~
24 | **/*.log
25 | **/.DS_Store
26 | **/Thumbs.db
27 |
--------------------------------------------------------------------------------
/.github/actions/generate-image-tags/action.yml:
--------------------------------------------------------------------------------
1 | name: 'Generate Image Tags'
2 |
3 | inputs:
4 | php:
5 | description: 'PHP version and (optional) variant'
6 | required: true
7 | octane-runtime:
8 | description: 'Octane runtime and version'
9 | required: false
10 |
11 | outputs:
12 | tags:
13 | description: 'JSON-encoded generated tags'
14 | value: ${{ steps.tags.outputs.tags }}
15 |
16 | runs:
17 | using: composite
18 | steps:
19 | - name: Generate Tags
20 | id: tags
21 | shell: bash
22 | working-directory: ${{ github.action_path }}
23 | run: |
24 | echo "tags=$(php generate-image-tags.php ${{ inputs.php }} ${{ inputs.octane-runtime }})" >> $GITHUB_OUTPUT
25 |
--------------------------------------------------------------------------------
/8.x/Dockerfile.octane.frankenphp:
--------------------------------------------------------------------------------
1 | ARG PHP_VERSION
2 |
3 | # Workaround as `COPY --from` doesn't support argument interpolation
4 | FROM dunglas/frankenphp:latest-php${PHP_VERSION}-alpine AS frankenphp
5 |
6 | FROM ghcr.io/iksaku/laravel-alpine:${PHP_VERSION}-zts
7 |
8 | RUN apk add --no-cache ca-certificates
9 |
10 | # Overwrite entrypoint to use Octane
11 | RUN sed -i \
12 | 's/artisan serve/artisan octane:start --server=frankenphp --admin-port=2019/' \
13 | /usr/local/bin/start-server
14 |
15 | # Install FrankenPHP binary
16 | COPY --from=frankenphp /usr/local/bin/frankenphp /usr/local/bin/frankenphp
17 |
18 | RUN setcap "cap_net_bind_service=+ep" /usr/local/bin/frankenphp
19 |
20 | EXPOSE 443
21 | EXPOSE 443/udp
22 | EXPOSE 2019
23 |
--------------------------------------------------------------------------------
/.idea/php.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/8.x/shared/php.ini:
--------------------------------------------------------------------------------
1 | [PHP]
2 | expose_php = Off
3 | display_errors = Off
4 | display_startup_errors = Off
5 |
6 | log_errors = On
7 | ; /dev/stdout goes to /dev/null so don't use that
8 | error_log = /dev/stderr
9 |
10 | max_execution_time = 60
11 |
12 | post_max_size = 100M
13 | upload_max_filesize = 100M
14 | memory_limit = 128M
15 |
16 | ; This directive determines which super global arrays are registered when PHP
17 | ; starts up. G,P,C,E & S are abbreviations for the following respective super
18 | ; globals: GET, POST, COOKIE, ENV and SERVER. There is a performance penalty
19 | ; paid for the registration of these arrays and because ENV is not as commonly
20 | ; used as the others, ENV is not recommended on productions servers. You
21 | ; can still get access to the environment variables through getenv() should you
22 | ; need to.
23 | ; Default Value: "EGPCS"
24 | ; Development Value: "GPCS"
25 | ; Production Value: "GPCS";
26 | ; http://php.net/variables-order
27 | variables_order = EGPCS
28 |
--------------------------------------------------------------------------------
/8.x/shared/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | if [ "$LARAVEL_SAIL" = '1' ]; then
4 | if ! id 'sail' &>/dev/null; then
5 | useradd -MNo -g laravel -u $(id -u laravel) sail
6 | fi
7 |
8 | if [ ! -z "$WWWUSER" ]; then
9 | usermod -ou $WWWUSER sail &>/dev/null
10 | fi
11 | fi
12 |
13 | if [ ! -z "$WWWUSER" ]; then
14 | usermod -ou $WWWUSER laravel &>/dev/null
15 | fi
16 |
17 | if [ ! -z "$WWWGROUP" ]; then
18 | groupmod -og $WWWGROUP laravel &>/dev/null
19 | fi
20 |
21 | chown -R laravel:laravel $APP_DIRECTORY
22 |
23 | if [ $# -gt 0 ]; then
24 | # Execute given command under container's user.
25 | su-exec laravel "$@"
26 | else
27 | if [ -d $APP_DIRECTORY/.deploy ] && [ "$LARAVEL_SAIL" != '1' ] && [ "$RUN_DEPLOY_SCRIPTS" = '1' ]; then
28 | for SCRIPT in $APP_DIRECTORY/.deploy/*.sh; do
29 | su-exec laravel sh $SCRIPT
30 | done
31 | fi
32 |
33 | # If no command is given, execute start script.
34 | su-exec laravel /usr/local/bin/start-server
35 | fi
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Jorge González
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/8.x/Dockerfile:
--------------------------------------------------------------------------------
1 | ARG PHP_VERSION
2 | ARG VARIANT
3 | FROM php:${PHP_VERSION}-${VARIANT}-alpine as base
4 |
5 | ENV APP_DIRECTORY=/var/www/html
6 |
7 | WORKDIR ${APP_DIRECTORY}
8 |
9 | # Get php extension installer
10 | ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
11 | RUN chmod +x /usr/local/bin/install-php-extensions
12 |
13 | # Install system dependencies
14 | RUN set -eux \
15 | && apk --update add --no-cache --purge \
16 | # Required to serve our Laravel application
17 | libcap \
18 | su-exec \
19 | # Compatibility layer for user and group commands
20 | shadow \
21 | # Install missing PHP extensions required by Laravel
22 | && install-php-extensions \
23 | @composer \
24 | bcmath \
25 | intl \
26 | pcntl \
27 | mysqli pdo_mysql \
28 | pgsql pdo_pgsql \
29 | redis
30 |
31 | RUN setcap "cap_net_bind_service=+ep" /usr/local/bin/php
32 |
33 | COPY shared/entrypoint.sh /usr/local/bin/entrypoint
34 | COPY shared/start-server.sh /usr/local/bin/start-server
35 | RUN chmod +x /usr/local/bin/entrypoint /usr/local/bin/start-server
36 |
37 | COPY shared/php.ini /usr/local/etc/php/conf.d/php.ini
38 |
39 | # Install Laravel Scheduler to crontab.
40 | # Expected to be run crond as root, then su-exec takes over.
41 | COPY shared/crontab /var/spool/cron/crontabs/root
42 |
43 | RUN useradd -ms /bin/sh -u 1337 -U laravel
44 |
45 | EXPOSE 80
46 |
47 | ENTRYPOINT ["entrypoint"]
48 |
--------------------------------------------------------------------------------
/.github/actions/version-outdated/action.yml:
--------------------------------------------------------------------------------
1 | name: 'Check if version is outdated'
2 |
3 | inputs:
4 | tool-name:
5 | description: 'Name of the tool to echo results of'
6 | required: true
7 | base:
8 | description: 'Base (current) version for comparison'
9 | required: true
10 | head:
11 | description: 'Head (newer) version for comparison'
12 | required: true
13 |
14 | outputs:
15 | outdated:
16 | description: |
17 | Whether the provided "Base version" is up to date with "Head version".
18 | If "Base version" or "Head version" is empty, then we will mark "outdated" as "true".
19 | value: ${{ steps.compare.outputs.outdated }}
20 |
21 | runs:
22 | using: composite
23 | steps:
24 | - name: 'Compare Versions'
25 | id: compare
26 | shell: bash
27 | run: |
28 | if [ -z "${{ inputs.base }}" ] || [ -z "${{ inputs.head }}" ]; then
29 | echo "outdated=true" >> $GITHUB_OUTPUT
30 | else
31 | echo "outdated=$(php -r 'var_export(version_compare("${{ inputs.head }}", "${{ inputs.base }}", ">"));')" >> $GITHUB_OUTPUT
32 | fi
33 | - name: 'Output results'
34 | shell: bash
35 | run: |
36 | if [ -z "${{ inputs.base }}" ] || [ -z "${{ inputs.head }}" ]; then
37 | echo "${{ inputs.tool-name }} (${{ inputs.base || inputs.head }}) appears to be new!."
38 | elif [ "${{ steps.compare.outputs.outdated }}" == "true" ]; then
39 | echo "A new version of ${{ inputs.tool-name }} is available (${{ inputs.base }} -> ${{ inputs.head }})."
40 | else
41 | echo "${{ inputs.tool-name }} is up to date (${{ inputs.base }})."
42 | fi
--------------------------------------------------------------------------------
/.github/actions/runtime-version-compare/action.yml:
--------------------------------------------------------------------------------
1 | name: 'Check if runtime is up to date'
2 |
3 | inputs:
4 | runtime-name:
5 | description: 'Name of the runtime to print debug messages'
6 | required: true
7 | base-image:
8 | description: 'The base image that may or may not be up to date'
9 | required: true
10 | upstream-image:
11 | description: 'The upstream image that should always be up to date'
12 | required: true
13 | version-command:
14 | description: 'Shared command used to extract runtime versions from both base and upstream images.'
15 | required: false
16 |
17 | outputs:
18 | outdated:
19 | description: 'Whether the runtime is up to date'
20 | value: ${{ steps.compare.outputs.outdated }}
21 | version:
22 | description: |
23 | Most up to date runtime version.
24 | If upstream-version results in a falsy (empty) value, use the base-version instead.
25 | value: ${{ steps.upstream-version.outputs.version || steps.base-version.outputs.version }}
26 |
27 | runs:
28 | using: composite
29 | steps:
30 | - name: 'Get base runtime version'
31 | id: base-version
32 | shell: bash
33 | run: echo "version=$(docker run --rm ${{ inputs.base-image }} ${{ inputs.version-command }})" >> $GITHUB_OUTPUT
34 |
35 | - name: 'Get upstream runtime version'
36 | id: upstream-version
37 | shell: bash
38 | run: echo "version=$(docker run --rm ${{ inputs.upstream-image }} ${{ inputs.version-command }})" >> $GITHUB_OUTPUT
39 |
40 | - name: 'Compare runtime versions'
41 | if: ${{ !cancelled() }}
42 | id: compare
43 | uses: ./.github/actions/version-outdated
44 | with:
45 | tool-name: ${{ inputs.runtime-name }}
46 | base: ${{ steps.base-version.outputs.version }}
47 | head: ${{ steps.upstream-version.outputs.version }}
48 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### VisualStudioCode template
2 | .vscode/*
3 | !.vscode/settings.json
4 | !.vscode/tasks.json
5 | !.vscode/launch.json
6 | !.vscode/extensions.json
7 | !.vscode/*.code-snippets
8 |
9 | # Local History for Visual Studio Code
10 | .history/
11 |
12 | # Built Visual Studio Code Extensions
13 | *.vsix
14 |
15 | ### JetBrains template
16 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
17 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
18 |
19 | # User-specific stuff
20 | .idea/**/workspace.xml
21 | .idea/**/tasks.xml
22 | .idea/**/usage.statistics.xml
23 | .idea/**/dictionaries
24 | .idea/**/shelf
25 |
26 | # AWS User-specific
27 | .idea/**/aws.xml
28 |
29 | # Generated files
30 | .idea/**/contentModel.xml
31 |
32 | # Sensitive or high-churn files
33 | .idea/**/dataSources/
34 | .idea/**/dataSources.ids
35 | .idea/**/dataSources.local.xml
36 | .idea/**/sqlDataSources.xml
37 | .idea/**/dynamic.xml
38 | .idea/**/uiDesigner.xml
39 | .idea/**/dbnavigator.xml
40 |
41 | # Gradle
42 | .idea/**/gradle.xml
43 | .idea/**/libraries
44 |
45 | # Gradle and Maven with auto-import
46 | # When using Gradle or Maven with auto-import, you should exclude module files,
47 | # since they will be recreated, and may cause churn. Uncomment if using
48 | # auto-import.
49 | # .idea/artifacts
50 | # .idea/compiler.xml
51 | # .idea/jarRepositories.xml
52 | # .idea/modules.xml
53 | # .idea/*.iml
54 | # .idea/modules
55 | # *.iml
56 | # *.ipr
57 |
58 | # CMake
59 | cmake-build-*/
60 |
61 | # Mongo Explorer plugin
62 | .idea/**/mongoSettings.xml
63 |
64 | # File-based project format
65 | *.iws
66 |
67 | # IntelliJ
68 | out/
69 |
70 | # mpeltonen/sbt-idea plugin
71 | .idea_modules/
72 |
73 | # JIRA plugin
74 | atlassian-ide-plugin.xml
75 |
76 | # Cursive Clojure plugin
77 | .idea/replstate.xml
78 |
79 | # SonarLint plugin
80 | .idea/sonarlint/
81 |
82 | # Crashlytics plugin (for Android Studio and IntelliJ)
83 | com_crashlytics_export_strings.xml
84 | crashlytics.properties
85 | crashlytics-build.properties
86 | fabric.properties
87 |
88 | # Editor-based Rest Client
89 | .idea/httpRequests
90 |
91 | # Android studio 3.1+ serialized cache file
92 | .idea/caches/build_file_checksums.ser
93 |
94 |
--------------------------------------------------------------------------------
/.github/actions/generate-image-tags/generate-image-tags.php:
--------------------------------------------------------------------------------
1 | priority = Tag::$nextPriority--;
13 | }
14 |
15 | public function append(string $append): Tag {
16 | $this->value .= $append;
17 |
18 | return $this;
19 | }
20 |
21 | public function suffix(string $suffix): Tag {
22 | $this->suffix = $suffix;
23 |
24 | return $this;
25 | }
26 |
27 | public function __clone(): void {
28 | $this->priority = Tag::$nextPriority--;
29 | }
30 |
31 | public function __toString(): string {
32 | $properties = [
33 | 'type' => 'raw',
34 | 'value' => $this->value,
35 | ];
36 |
37 | if (!is_null($this->prefix) && trim($this->prefix) !== '') {
38 | $properties['prefix'] = $this->prefix;
39 | }
40 |
41 | if (!is_null($this->suffix) && trim($this->suffix) !== '') {
42 | $properties['suffix'] = $this->suffix;
43 | }
44 |
45 | if ($this->priority > 0) {
46 | $properties['priority'] = $this->priority;
47 | }
48 |
49 | return implode(',', array_map(
50 | fn (mixed $value, string $key) => "{$key}={$value}",
51 | $properties,
52 | array_keys($properties)
53 | ));
54 | }
55 | }
56 |
57 | function destructure_defaults(int $values, array $arr): array {
58 | return array_replace(
59 | array_fill(0, $values, ''),
60 | $arr
61 | );
62 | }
63 |
64 | function generate_semver_tags(string $semver): array {
65 | $parts = explode('.', $semver);
66 |
67 | if (count($parts) === 1) return $parts;
68 |
69 | return array_map(
70 | fn (int $length) => implode('.', array_slice($parts, 0, $length)),
71 | range(2, count($parts))
72 | );
73 | }
74 |
75 | [$php_version, $octane_runtime] = destructure_defaults(2, array_slice($argv, 1));
76 |
77 | [$php_version, $php_variant] = destructure_defaults(2, explode(':', $php_version));
78 |
79 | $tags = array_map(
80 | fn (string $semver) => new Tag(value: $semver),
81 | generate_semver_tags($php_version)
82 | );
83 |
84 | if (trim($php_variant) !== '') {
85 | if ($php_variant === 'cli') {
86 | $tags = array_merge(
87 | $tags,
88 | array_map(
89 | fn (Tag $tag) => (clone $tag)->append('-cli'),
90 | $tags
91 | )
92 | );
93 | } else {
94 | array_walk(
95 | $tags,
96 | fn (Tag $tag) => $tag->append("-{$php_variant}")
97 | );
98 | }
99 | } else if (trim($octane_runtime) !== '') {
100 | if (!str_contains($octane_runtime, ':')) {
101 | throw new \RuntimeException('Octane Runtime is missing version.');
102 | }
103 |
104 | [$octane_runtime, $octane_runtime_version] = explode(':', $octane_runtime);
105 |
106 | if (trim($octane_runtime_version) === '') {
107 | throw new \RuntimeException('Octane Runtime is missing version.');
108 | }
109 |
110 | $tags = array_merge(
111 | ...array_map(
112 | fn (Tag $tag) => [
113 | $tag->append("-octane-{$octane_runtime}"),
114 | ...array_map(
115 | fn (string $semver) => (clone $tag)->append("-{$semver}"),
116 | generate_semver_tags($octane_runtime_version)
117 | )
118 | ],
119 | $tags
120 | )
121 | );
122 | }
123 |
124 | $tags = array_merge(
125 | $tags,
126 | array_map(
127 | fn (Tag $tag) => (clone $tag)->suffix('-{{sha}}'),
128 | $tags
129 | )
130 | );
131 |
132 | usort($tags, fn (Tag $a, Tag $b) => $b->priority <=> $a->priority);
133 |
134 | echo json_encode(join(PHP_EOL, $tags));
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Laravel Alpine Docker Images
2 |
3 | This project provides Docker images containing the *bare minimum* requirements
4 | to run your Laravel application. We use PHP's official CLI/ZTS images based
5 | on Alpine Linux to further reduce the size of these images.
6 |
7 | You may notice that your favorite PHP extensions and NodeJS are _missing_ from
8 | these images, but this is a _feature_ ✨. The intention of these
9 | images is to provide the *absolute minimum requirements* so that you, the
10 | developer, pull these images, and further customize them with everything
11 | you need.
12 |
13 | ## Available Images
14 |
15 | You can choose the PHP version you want to run by specifying it as the tag for your image.
16 | Currently, we build base images for PHP versions `8.0` to `8.3`:
17 | * `ghcr.io/iksaku/laravel-alpine:8.0`
18 | * `ghcr.io/iksaku/laravel-alpine:8.1`
19 | * `ghcr.io/iksaku/laravel-alpine:8.2`
20 | * `ghcr.io/iksaku/laravel-alpine:8.3`
21 |
22 | ### Laravel Octane Images
23 |
24 | We also build images compatible with [Laravel Octane](https://laravel.com/docs/octane),
25 | you can choose your favorite flavor from the following list:
26 |
27 | | Runtime | Supported PHP versions | Image name example |
28 | | ------------ | ---------------------- | ----------------------------------------------------- |
29 | | `frankenphp` | `8.2`-`8.3` | `ghcr.io/iksaku/laravel-alpine:8.3-octane-frankenphp` |
30 | | `openswoole` | `8.0`-`8.3` | `ghcr.io/iksaku/laravel-alpine:8.3-octane-openswoole` |
31 | | `roadrunner` | `8.0`-`8.3` | `ghcr.io/iksaku/laravel-alpine:8.3-octane-roadrunner` |
32 | | `swoole` | `8.0`-`8.3` | `ghcr.io/iksaku/laravel-alpine:8.3-octane-swoole` |
33 |
34 | ### Available PHP extensions
35 |
36 | Pre-installed PHP extensions in these images follow [Laravel's Requirements](https://laravel.com/docs/deployment#server-requirements)
37 | and also include a few extras for Database and Octane support:
38 |
39 | | Name | Availability |
40 | | -------------- | ------------------------ |
41 | | bcmath | ✓ |
42 | | ctype | ✓ |
43 | | curl | ✓ |
44 | | dom | ✓ |
45 | | fileinfo | ✓ |
46 | | intl | ✓ |
47 | | json | ✓ |
48 | | mbstring | ✓ |
49 | | mysqli | ✓ |
50 | | openssl | ✓ |
51 | | pcre | ✓ |
52 | | pdo | ✓ |
53 | | pdo_mysql | ✓ |
54 | | pdo_pgsql | ✓ |
55 | | pdo_sqlite | ✓ |
56 | | pgsql | ✓ |
57 | | redis | ✓ |
58 | | sqlite3 | ✓ |
59 | | tokenizer | ✓ |
60 | | xml | ✓ |
61 | | composer | ✓ |
62 | | pcntl | ✓ |
63 | | openswoole | Octane-only (OpenSwoole) |
64 | | sockets | Octane-only (RoadRunner) |
65 | | swoole | Octane-only (Swoole) |
66 |
67 | > [!TIP]
68 | > You can always view the list of installed extensions from your terminal:
69 | > `docker run --rm ghcr.io/iksaku/laravel-alpine:8.1 php -m`
70 |
71 | ### Installing additional PHP extensions
72 |
73 | Our images come with [mlocati's `install-php-extension`](https://github.com/mlocati/docker-php-extension-installer)
74 | binary available, so you can install additional PHP extensions that you may need:
75 |
76 | ```dockerfile
77 | FROM ghcr.io/iksaku/laravel-alpine:8.1
78 |
79 | RUN install-php-extension \
80 | ffi \
81 | vips \
82 | yaml
83 | ```
84 |
85 | You can see all available extensions at [`install-php-extension`'s repo](https://github.com/mlocati/docker-php-extension-installer#supported-php-extensions).
86 |
87 | ## Running on Laravel Sail
88 |
89 | As opposed to [Laravel Sail](https://laravel.com/docs/sail), you don't need to import
90 | this repository as a package nor _publish_ our `Dockerfile` assets most of the time,
91 | you can use our images directly in your `docker-compose.yml` file or use them
92 | as a base for your custom `Dockerfile`.
93 |
94 | ### About container permissions
95 | Before proceeding, it is important to know that our images dynamically adjust Group and User
96 | permissions when your container _starts_, while Laravel Sail adjust the Group permissions
97 | _on build_, so the first change you must make is to move the `WWWGROUP` argument to be
98 | part of the `environment` variables of your `docker-compose.yml` file:
99 | ```diff
100 | services:
101 | laravel.test:
102 | build:
103 | context: ./vendor/laravel/sail/runtimes/8.1
104 | - args:
105 | - WWWGROUP: '${WWWGROUP}'
106 | # ...
107 | envrionment:
108 | WWWUSER: '${WWWUSER}'
109 | + WWWGROUP: '${WWWGROUP}'
110 | # ...
111 | ```
112 |
113 | ### Running base images
114 | To run our images in Laravel Sail, you can replace the `build` option with `image`
115 | in your `docker-compose.yml` file:
116 | ```diff
117 | services:
118 | laravel.test:
119 | - build:
120 | - context: ./vendor/laravel/sail/runtimes/8.1
121 | + image: ghcr.io/iksaku/laravel-alpine:8.1
122 | ```
123 |
124 | > [!TIP]
125 | > You can always update to the latest version of your chosen image
126 | > using `sail pull` 📥.
127 |
128 | ### Running octane images
129 | Octane images also run in Laravel Sail; you need to change your `image`
130 | reference to specify you want to run octane:
131 | ```diff
132 | services:
133 | laravel.test:
134 | - image: ghcr.io/iksaku/laravel-alpine:8.1
135 | + image: ghcr.io/iksaku/laravel-alpine:8.1-octane-roadrunner
136 | ```
137 |
138 | ### Running modified images
139 | If you want to customize your image further, then work with the `build` option in your
140 | `docker-compose.yml` file:
141 |
142 | ```sh
143 | super-duper-project
144 | ├── app
145 | ├── bootstrap
146 | │ ...
147 | ├── docker
148 | │ └── super-duper-runtime
149 | │ └── Dockerfile
150 | ├── docker-compose.yml
151 | │ ...
152 | ```
153 |
154 | ```diff
155 | services:
156 | laravel.test:
157 | build:
158 | - image: ghcr.io/iksaku/laravel-alpine:8.1
159 | + context: ./docker/super-duper-runtime
160 | ```
161 |
162 | ## Deploying to Production
163 |
164 | Published images can also be used in production, but to run the best
165 | in-class service, it is 100% recommended that you take a look into customizing
166 | the image to your needs, as well as to make sure that the build process is
167 | tailored to your needs.
168 |
169 | The following script can be a good starting point for most projects:
170 |
171 | ```dockerfile
172 | # Based on @fideloper's fly.io laravel template
173 | # https://github.com/superfly/flyctl/blob/94fec0925c75cfe30921f1a4df25fa9cbf2877e9/scanner/templates/laravel/Dockerfile
174 |
175 | FROM ghcr.io/iksaku/laravel-alpine:8.1
176 |
177 | # Run the installed Laravel Scheduler in the background.
178 | # You can replace the default entry by copying your own crontab
179 | # file in container's /var/spool/cron/crontabs/root
180 | RUN crond
181 |
182 | # Copy our project files into the container
183 | COPY . /var/www/html
184 |
185 | # Install composer dependencies
186 | RUN composer install --optimize-autoloader --no-dev
187 |
188 | # Make sure our container has the correct permissions
189 | # to tap into our project storage
190 | RUN mkdir -p storage/logs \
191 | && chmod -R ug+w /var/www/html/storage \
192 | && chmod -R 755 /var/www/html
193 |
194 | # (Optional) Allow requests when running behind a proxy (i.e., fly.io)
195 | RUN sed -i 's/protected \$proxies/protected \$proxies = "*"/g' app/Http/Middleware/TrustProxies.php
196 | ```
197 |
198 | When deploying your code to different environments, commonly `staging` or `production`, a
199 | need to execute specific commands or script arises, mainly when dealing with Database migrations,
200 | linking storage folders, or performing a general app optimization.
201 |
202 | To help out with these tasks, our images support executing scripts inside a `.deploy`
203 | directory before running the Laravel server.
204 | To keep things simple, we do not check for a specific list of environments, instead we
205 | execute deployment scripts when a `RUN_DEPLOY_SCRIPTS` environment variable is available
206 | and has a value of `1`; otherwise, we ignore deployment scripts and jump straight
207 | into server execution.
208 |
209 | > [!IMPORTANT]
210 | > `RUN_DEPLOY_SCRIPTS` is ignored when running in Laravel Sail to prevent unintended
211 | > side effects.
212 |
213 | If you have multiple scripts, you can number them in the order they should be executed:
214 |
215 | ```
216 | my-laravel-app/
217 | ├── .deploy/
218 | │ ├── 01_migrate_database.sh
219 | │ ├── 02_optimize_application.sh
220 | ├── app/
221 | ├── bootstrap/
222 | │ ...
223 | ```
224 |
225 | > [!IMPORTANT]
226 | > Deploy scripts should be suffixed with the `.sh` extension and will be run using
227 | > Alpine's `sh` shell as we do not have `bash`.
228 |
229 | ### Deploying to Fly.io
230 |
231 | It is recommended that when you deploy your code to a production environment you
232 | run the following commands:
233 | * `php artisan optimize`
234 | * `php artisan migrate --force`
235 |
236 | It is easy to add such commands to a before/after deploy hook on most cases, but
237 | when deploying to [Fly.io](https://fly.io/) it is rather troublesome that you can't
238 | execute these commands in your `Dockerfile`, as it has no access to envrionment variables
239 | during build.
240 |
241 | Another way to execute deployment commands when deploying to Fly.io is by creating a
242 | `on_deploy.sh` script on your app's root directory, and then calling this script from
243 | your `fly.toml` file using the [`deploy.release_command`](https://fly.io/docs/reference/configuration/#run-one-off-commands-before-releasing-a-deployment)
244 | property:
245 |
246 | ```sh
247 | #!/usr/bin/env sh
248 |
249 | php artisan migrate --force
250 | ```
251 |
252 | ```toml
253 | # fly.toml
254 |
255 | # ...
256 |
257 | [deploy]
258 | release_command = "sh ./on_deploy.sh"
259 |
260 | # ...
261 | ```
262 |
263 | When using this method, Fly will spawn a _temporary_ (or _ephemeral_) virtual machine with
264 | access to your app's environment to execute this script, and later on, it will destroy such
265 | VM and queue another VM to take over the given environment. This means that any file-based
266 | changes done while executing the `release_command` will not be persisted, so only procedures
267 | that ping other services not in the VM, like Database migrations, will persist.
268 |
269 | Use the above mentioned `.deploy` directory if you are planning to execute commands like
270 | `artisan storage:link` or `artisan optimize`.
271 |
272 | > [!NOTE]
273 | > The image's default entrypoint will manage this command, making
274 | > your script execution to be done by the `laravel` user, which is the
275 | > default one configured with all app permissions in your container.
276 |
277 | #### Running multiple processes
278 | Fly's support for [multiple process groups](https://fly.io/docs/apps/processes/) allow to run multiple
279 | commands (or processes) in separate machines. This is useful when you want also run a queue worker or
280 | a scheduler:
281 |
282 | ```toml
283 | # fly.toml
284 |
285 | # ...
286 |
287 | [processes]
288 | # This is the default process group running our Laravel app
289 | app = ""
290 | # Run the Laravel scheduler using Alpine's crond in the foreground (for logs)
291 | scheduler = "crond -f"
292 | # Run the Laravel queue worker
293 | worker = "php artisan queue:work"
294 | ```
295 |
296 | For more information on how process groups work and how you can scale them, check out:
297 | * [Run Multiple Process Groups in an App](https://fly.io/docs/apps/processes/).
298 | * [Scale Process Groups](https://fly.io/docs/apps/scale-count/#scale-by-process-group).
299 |
300 | ## Credits
301 |
302 | As this intends to be an alternative version of Laravel Sail images, most of the credit
303 | goes to the [@laravel](https://laravel.com/team) team itself for building and
304 | maintaining Laravel Sail for us 💖.
305 |
306 | Also, much inspiration (and snippets) come from [@fideloper](https://github.com/fideloper)'s
307 | public work on [Fly.io's Laravel Template](https://github.com/superfly/flyctl) 🔮.
308 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Image Builder
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths-ignore:
8 | - '**.md'
9 | pull_request:
10 | paths-ignore:
11 | - '**.md'
12 | schedule:
13 | - cron: '0 7 * * 1'
14 |
15 | jobs:
16 | build:
17 | name: php-${{ matrix.php }}-${{ matrix.variant }}
18 | runs-on: ubuntu-latest
19 | timeout-minutes: 20
20 | strategy:
21 | matrix:
22 | php:
23 | - '8.0'
24 | - '8.1'
25 | - '8.2'
26 | - '8.3'
27 | variant:
28 | - cli
29 | - zts
30 | steps:
31 | - name: Checkout Code
32 | uses: actions/checkout@v4
33 |
34 | - name: Check if PHP version is up to date
35 | id: php
36 | uses: ./.github/actions/runtime-version-compare
37 | with:
38 | runtime-name: PHP ${{ matrix.php }} (${{ matrix.variant }})
39 | base-image: ghcr.io/iksaku/laravel-alpine:${{ matrix.php }}-${{ matrix.variant }}
40 | upstream-image: php:${{ matrix.php }}-${{ matrix.variant }}-alpine
41 | version-command: php -r 'echo PHP_VERSION;'
42 |
43 | - name: Get Docker Context
44 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' }}
45 | run: echo "DOCKER_BUILD_CONTEXT=$(echo ${{ matrix.php }} | sed -r 's/^([0-9]+)\..*$/\1.x/')" >> $GITHUB_ENV
46 |
47 | - name: Generate Image Tags
48 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' }}
49 | id: tags
50 | uses: ./.github/actions/generate-image-tags
51 | with:
52 | php: ${{ steps.php.outputs.version }}:${{ matrix.variant }}
53 |
54 | - name: Generate Image Metadata
55 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' }}
56 | id: meta
57 | uses: docker/metadata-action@v5
58 | with:
59 | images: ghcr.io/iksaku/laravel-alpine
60 | tags: ${{ fromJSON(steps.tags.outputs.tags) }}
61 |
62 | - name: Set up QEMU
63 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' }}
64 | uses: docker/setup-qemu-action@v3
65 |
66 | - name: Set up Docker Buildx
67 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' }}
68 | uses: docker/setup-buildx-action@v3
69 |
70 | - name: Login to GitHub Container Registry
71 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' }}
72 | uses: docker/login-action@v3
73 | with:
74 | registry: ghcr.io
75 | username: ${{ github.actor }}
76 | password: ${{ secrets.GITHUB_TOKEN }}
77 |
78 | - name: Build and push
79 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' }}
80 | uses: docker/build-push-action@v5
81 | with:
82 | context: ${{ env.DOCKER_BUILD_CONTEXT }}
83 | build-args: |
84 | PHP_VERSION=${{ matrix.php }}
85 | VARIANT=${{ matrix.variant }}
86 | platforms: linux/amd64,linux/arm64
87 | push: ${{ contains(fromJSON('["push", "schedule"]'), github.event_name) && github.ref == 'refs/heads/main' }}
88 | tags: ${{ steps.meta.outputs.tags }}
89 |
90 | build-octane-frankenphp:
91 | name: php-${{ matrix.php }}-octane-frankenphp
92 | runs-on: ubuntu-latest
93 | timeout-minutes: 40
94 | needs: build
95 | strategy:
96 | matrix:
97 | php:
98 | - '8.2'
99 | - '8.3'
100 | steps:
101 | - name: Checkout Code
102 | uses: actions/checkout@v4
103 |
104 | - name: Check if PHP version is up to date
105 | id: php
106 | uses: ./.github/actions/runtime-version-compare
107 | with:
108 | runtime-name: PHP ${{ matrix.php }}
109 | base-image: ghcr.io/iksaku/laravel-alpine:${{ matrix.php }}-octane-frankenphp
110 | upstream-image: ghcr.io/iksaku/laravel-alpine:${{ matrix.php }}
111 | version-command: php -r 'echo PHP_VERSION;'
112 | - name: Check if FrankenPHP version is up to date
113 | id: frankenphp
114 | uses: ./.github/actions/runtime-version-compare
115 | with:
116 | runtime-name: Octane FrankenPHP
117 | base-image: ghcr.io/iksaku/laravel-alpine:${{ matrix.php }}-octane-frankenphp
118 | upstream-image: dunglas/frankenphp:latest-php${{ matrix.php }}-alpine
119 | version-command: frankenphp -v | sed -r 's/FrankenPHP v([0-9\.]+) .*/\1/'
120 |
121 | - name: Get Docker Context
122 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.frankenphp.outputs.outdated == 'true' }}
123 | run: echo "DOCKER_BUILD_CONTEXT=$(echo ${{ matrix.php }} | sed -r 's/^([0-9]+)\..*$/\1.x/')" >> $GITHUB_ENV
124 |
125 | - name: Generate Image Tags
126 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.frankenphp.outputs.outdated == 'true' }}
127 | id: tags
128 | uses: ./.github/actions/generate-image-tags
129 | with:
130 | php: ${{ steps.php.outputs.version }}
131 | octane-runtime: frankenphp:${{ steps.frankenphp.outputs.version }}
132 |
133 | - name: Generate Image Metadata
134 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.frankenphp.outputs.outdated == 'true' }}
135 | id: meta
136 | uses: docker/metadata-action@v5
137 | with:
138 | images: ghcr.io/iksaku/laravel-alpine
139 | tags: ${{ fromJSON(steps.tags.outputs.tags) }}
140 |
141 | - name: Set up QEMU
142 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.frankenphp.outputs.outdated == 'true' }}
143 | uses: docker/setup-qemu-action@v3
144 |
145 | - name: Set up Docker Buildx
146 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.frankenphp.outputs.outdated == 'true' }}
147 | uses: docker/setup-buildx-action@v3
148 |
149 | - name: Login to GitHub Container Registry
150 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.frankenphp.outputs.outdated == 'true' }}
151 | uses: docker/login-action@v3
152 | with:
153 | registry: ghcr.io
154 | username: ${{ github.actor }}
155 | password: ${{ secrets.GITHUB_TOKEN }}
156 |
157 | - name: Build and push
158 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.frankenphp.outputs.outdated == 'true' }}
159 | uses: docker/build-push-action@v5
160 | with:
161 | context: ${{ env.DOCKER_BUILD_CONTEXT }}
162 | file: ${{ env.DOCKER_BUILD_CONTEXT }}/Dockerfile.octane.frankenphp
163 | build-args: |
164 | PHP_VERSION=${{ matrix.php }}
165 | platforms: linux/amd64,linux/arm64
166 | push: ${{ contains(fromJSON('["push", "schedule"]'), github.event_name) && github.ref == 'refs/heads/main' }}
167 | tags: ${{ steps.meta.outputs.tags }}
168 |
169 | build-octane-openswoole:
170 | name: php-${{ matrix.php }}-octane-openswoole
171 | runs-on: ubuntu-latest
172 | timeout-minutes: 40
173 | needs: build
174 | strategy:
175 | matrix:
176 | php:
177 | - '8.0'
178 | - '8.1'
179 | - '8.2'
180 | - '8.3'
181 | steps:
182 | - name: Checkout Code
183 | uses: actions/checkout@v4
184 |
185 | - name: Check if PHP version is up to date
186 | id: php
187 | uses: ./.github/actions/runtime-version-compare
188 | with:
189 | runtime-name: PHP ${{ matrix.php }}
190 | base-image: ghcr.io/iksaku/laravel-alpine:${{ matrix.php }}-octane-openswoole
191 | upstream-image: ghcr.io/iksaku/laravel-alpine:${{ matrix.php }}
192 | version-command: php -r 'echo PHP_VERSION;'
193 | - name: Check if OpenSwoole version is up to date
194 | id: openswoole
195 | uses: ./.github/actions/runtime-version-compare
196 | with:
197 | runtime-name: Octane OpenSwoole
198 | base-image: ghcr.io/iksaku/laravel-alpine:${{ matrix.php }}-octane-openswoole
199 | upstream-image: openswoole/swoole:php${{ matrix.php }}-alpine
200 | version-command: php -r 'echo OPENSWOOLE_VERSION;'
201 |
202 | - name: Get Docker Context
203 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.openswoole.outputs.outdated == 'true' }}
204 | run: echo "DOCKER_BUILD_CONTEXT=$(echo ${{ matrix.php }} | sed -r 's/^([0-9]+)\..*$/\1.x/')" >> $GITHUB_ENV
205 |
206 | - name: Generate Image Tags
207 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.openswoole.outputs.outdated == 'true' }}
208 | id: tags
209 | uses: ./.github/actions/generate-image-tags
210 | with:
211 | php: ${{ steps.php.outputs.version }}
212 | octane-runtime: openswoole:${{ steps.openswoole.outputs.version }}
213 |
214 | - name: Generate Image Metadata
215 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.openswoole.outputs.outdated == 'true' }}
216 | id: meta
217 | uses: docker/metadata-action@v5
218 | with:
219 | images: ghcr.io/iksaku/laravel-alpine
220 | tags: ${{ fromJSON(steps.tags.outputs.tags) }}
221 |
222 | - name: Set up QEMU
223 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.openswoole.outputs.outdated == 'true' }}
224 | uses: docker/setup-qemu-action@v3
225 |
226 | - name: Set up Docker Buildx
227 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.openswoole.outputs.outdated == 'true' }}
228 | uses: docker/setup-buildx-action@v3
229 |
230 | - name: Login to GitHub Container Registry
231 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.openswoole.outputs.outdated == 'true' }}
232 | uses: docker/login-action@v3
233 | with:
234 | registry: ghcr.io
235 | username: ${{ github.actor }}
236 | password: ${{ secrets.GITHUB_TOKEN }}
237 |
238 | - name: Build and push
239 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.openswoole.outputs.outdated == 'true' }}
240 | uses: docker/build-push-action@v5
241 | with:
242 | context: ${{ env.DOCKER_BUILD_CONTEXT }}
243 | file: ${{ env.DOCKER_BUILD_CONTEXT }}/Dockerfile.octane.openswoole
244 | build-args: |
245 | PHP_VERSION=${{ matrix.php }}
246 | platforms: linux/amd64,linux/arm64
247 | push: ${{ contains(fromJSON('["push", "schedule"]'), github.event_name) && github.ref == 'refs/heads/main' }}
248 | tags: ${{ steps.meta.outputs.tags }}
249 |
250 | build-octane-roadrunner:
251 | name: php-${{ matrix.php }}-octane-roadrunner
252 | runs-on: ubuntu-latest
253 | timeout-minutes: 5
254 | needs: build
255 | strategy:
256 | matrix:
257 | php:
258 | - '8.0'
259 | - '8.1'
260 | - '8.2'
261 | - '8.3'
262 | steps:
263 | - name: Checkout Code
264 | uses: actions/checkout@v4
265 |
266 | - name: Check if PHP version is up to date
267 | id: php
268 | uses: ./.github/actions/runtime-version-compare
269 | with:
270 | runtime-name: PHP ${{ matrix.php }}
271 | base-image: ghcr.io/iksaku/laravel-alpine:${{ matrix.php }}-octane-roadrunner
272 | upstream-image: ghcr.io/iksaku/laravel-alpine:${{ matrix.php }}
273 | version-command: php -r 'echo PHP_VERSION;'
274 | - name: Check if RoadRunner version is up to date
275 | id: roadrunner
276 | uses: ./.github/actions/runtime-version-compare
277 | with:
278 | runtime-name: RoadRunner
279 | base-image: ghcr.io/iksaku/laravel-alpine:${{ matrix.php }}-octane-roadrunner rr
280 | upstream-image: ghcr.io/roadrunner-server/roadrunner:latest
281 | version-command: -v | sed -r 's/^rr version ([0-9\.]+) .*/\1/'
282 |
283 | - name: Get Docker Context
284 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.roadrunner.outputs.outdated == 'true' }}
285 | run: echo "DOCKER_BUILD_CONTEXT=$(echo ${{ matrix.php }} | sed -r 's/^([0-9]+)\..*$/\1.x/')" >> $GITHUB_ENV
286 |
287 | - name: Generate Image Tags
288 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.roadrunner.outputs.outdated == 'true' }}
289 | id: tags
290 | uses: ./.github/actions/generate-image-tags
291 | with:
292 | php: ${{ steps.php.outputs.version }}
293 | octane-runtime: roadrunner:${{ steps.roadrunner.outputs.version }}
294 |
295 | - name: Generate Image Metadata
296 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.roadrunner.outputs.outdated == 'true' }}
297 | id: meta
298 | uses: docker/metadata-action@v5
299 | with:
300 | images: ghcr.io/iksaku/laravel-alpine
301 | tags: ${{ fromJSON(steps.tags.outputs.tags) }}
302 |
303 | - name: Set up QEMU
304 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.roadrunner.outputs.outdated == 'true' }}
305 | uses: docker/setup-qemu-action@v3
306 |
307 | - name: Set up Docker Buildx
308 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.roadrunner.outputs.outdated == 'true' }}
309 | uses: docker/setup-buildx-action@v3
310 |
311 | - name: Login to GitHub Container Registry
312 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.roadrunner.outputs.outdated == 'true' }}
313 | uses: docker/login-action@v3
314 | with:
315 | registry: ghcr.io
316 | username: ${{ github.actor }}
317 | password: ${{ secrets.GITHUB_TOKEN }}
318 |
319 | - name: Build and push
320 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.roadrunner.outputs.outdated == 'true' }}
321 | uses: docker/build-push-action@v5
322 | with:
323 | context: ${{ env.DOCKER_BUILD_CONTEXT }}
324 | file: ${{ env.DOCKER_BUILD_CONTEXT }}/Dockerfile.octane.roadrunner
325 | build-args: |
326 | PHP_VERSION=${{ matrix.php }}
327 | platforms: linux/amd64,linux/arm64
328 | push: ${{ contains(fromJSON('["push", "schedule"]'), github.event_name) && github.ref == 'refs/heads/main' }}
329 | tags: ${{ steps.meta.outputs.tags }}
330 |
331 | build-octane-swoole:
332 | name: php-${{ matrix.php }}-octane-swoole
333 | runs-on: ubuntu-latest
334 | timeout-minutes: 40
335 | needs: build
336 | strategy:
337 | matrix:
338 | php:
339 | - '8.0'
340 | - '8.1'
341 | - '8.2'
342 | - '8.3'
343 | steps:
344 | - name: Checkout Code
345 | uses: actions/checkout@v4
346 |
347 | - name: Check if PHP version is up to date
348 | id: php
349 | uses: ./.github/actions/runtime-version-compare
350 | with:
351 | runtime-name: PHP ${{ matrix.php }}
352 | base-image: ghcr.io/iksaku/laravel-alpine:${{ matrix.php }}-octane-swoole
353 | upstream-image: ghcr.io/iksaku/laravel-alpine:${{ matrix.php }}
354 | version-command: php -r 'echo PHP_VERSION;'
355 | - name: Check if Swoole version is up to date
356 | id: swoole
357 | uses: ./.github/actions/runtime-version-compare
358 | with:
359 | runtime-name: Octane Swoole
360 | base-image: ghcr.io/iksaku/laravel-alpine:${{ matrix.php }}-octane-swoole
361 | upstream-image: phpswoole/swoole:php${{ matrix.php }}-alpine
362 | version-command: php -r 'echo swoole_version();'
363 |
364 | - name: Get Docker Context
365 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.swoole.outputs.outdated == 'true' }}
366 | run: echo "DOCKER_BUILD_CONTEXT=$(echo ${{ matrix.php }} | sed -r 's/^([0-9]+)\..*$/\1.x/')" >> $GITHUB_ENV
367 |
368 | - name: Generate Image Tags
369 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.swoole.outputs.outdated == 'true' }}
370 | id: tags
371 | uses: ./.github/actions/generate-image-tags
372 | with:
373 | php: ${{ steps.php.outputs.version }}
374 | octane-runtime: swoole:${{ steps.swoole.outputs.version }}
375 |
376 | - name: Generate Image Metadata
377 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.swoole.outputs.outdated == 'true' }}
378 | id: meta
379 | uses: docker/metadata-action@v5
380 | with:
381 | images: ghcr.io/iksaku/laravel-alpine
382 | tags: ${{ fromJSON(steps.tags.outputs.tags) }}
383 |
384 | - name: Set up QEMU
385 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.swoole.outputs.outdated == 'true' }}
386 | uses: docker/setup-qemu-action@v3
387 |
388 | - name: Set up Docker Buildx
389 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.swoole.outputs.outdated == 'true' }}
390 | uses: docker/setup-buildx-action@v3
391 |
392 | - name: Login to GitHub Container Registry
393 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.swoole.outputs.outdated == 'true' }}
394 | uses: docker/login-action@v3
395 | with:
396 | registry: ghcr.io
397 | username: ${{ github.actor }}
398 | password: ${{ secrets.GITHUB_TOKEN }}
399 |
400 | - name: Build and push
401 | if: ${{ github.event_name != 'schedule' || steps.php.outputs.outdated == 'true' || steps.swoole.outputs.outdated == 'true' }}
402 | uses: docker/build-push-action@v5
403 | with:
404 | context: ${{ env.DOCKER_BUILD_CONTEXT }}
405 | file: ${{ env.DOCKER_BUILD_CONTEXT }}/Dockerfile.octane.swoole
406 | build-args: |
407 | PHP_VERSION=${{ matrix.php }}
408 | platforms: linux/amd64,linux/arm64
409 | push: ${{ contains(fromJSON('["push", "schedule"]'), github.event_name) && github.ref == 'refs/heads/main' }}
410 | tags: ${{ steps.meta.outputs.tags }}
411 |
--------------------------------------------------------------------------------