This repository contains a base image with Alpine + PHP + Caddy, which you can use to build your docker image with your code.
Note
This docker image expects that you install all extensions using Composer. Otherwise, you will get The class X is not found
errors. See docs for more information
Create a Dockerfile in your project like:
#syntax=docker/dockerfile:1.4
# pin versions
FROM ghcr.io/shopware/docker-base:8.2 as base-image
FROM ghcr.io/friendsofshopware/shopware-cli:latest-php-8.2 as shopware-cli
# build
FROM shopware-cli as build
COPY --link . /src
WORKDIR /src
RUN --mount=type=secret,id=composer_auth,dst=/src/auth.json \
--mount=type=cache,target=/root/.composer \
--mount=type=cache,target=/root/.npm \
/usr/local/bin/entrypoint.sh shopware-cli project ci /src
# build final image
FROM base-image
COPY --from=build --chown=www-data /src /var/www/html
or better run composer req shopware/docker
to install the Symfony Recipe.
In the stage build
we are using shopware-cli to build the Shopware files:
- Composer installs
- Build Administration and Storefront with Extensions if needed
- Strip some files of vendor to make the layer small
- Merge administration snippets into one file
Refer to shopware-cli to learn more about shopware-cli project ci
You build your individual docker image with the source code in your CI pipeline and push it to your Container Registry. Later on can you use this image inside example docker-compose / kubernetes / etc.
Example Github Action to build the image
name: Build Docker Image
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login into Github Docker Registery
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
file: ./docker/Dockerfile
push: true
tags: ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}
Example docker-compose
version: "3.8"
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: 'shopware'
MYSQL_USER: shopware
MYSQL_PASSWORD: shopware
MYSQL_DATABASE: shopware
volumes:
- mysql-data:/var/lib/mysql
init-perm:
image: alpine
volumes:
- files:/var/www/html/files
- theme:/var/www/html/public/theme
- media:/var/www/html/public/media
- thumbnail:/var/www/html/public/thumbnail
- sitemap:/var/www/html/public/sitemap
command: chown 82:82 /var/www/html/files /var/www/html/public/theme /var/www/html/public/media /var/www/html/public/thumbnail /var/www/html/public/sitemap
init:
image: local
build:
context: .
env_file: .app.env
entrypoint: /setup
volumes:
- files:/var/www/html/files
- theme:/var/www/html/public/theme
- media:/var/www/html/public/media
- thumbnail:/var/www/html/public/thumbnail
- sitemap:/var/www/html/public/sitemap
depends_on:
db:
condition: service_started
init-perm:
condition: service_completed_successfully
web:
image: local
build:
context: .
volumes:
- files:/var/www/html/files
- theme:/var/www/html/public/theme
- media:/var/www/html/public/media
- thumbnail:/var/www/html/public/thumbnail
- sitemap:/var/www/html/public/sitemap
depends_on:
init:
condition: service_completed_successfully
env_file: .app.env
ports:
- 8000:8000
worker:
image: local
restart: unless-stopped
build:
context: .
volumes:
- files:/var/www/html/files
- theme:/var/www/html/public/theme
- media:/var/www/html/public/media
- thumbnail:/var/www/html/public/thumbnail
- sitemap:/var/www/html/public/sitemap
depends_on:
init:
condition: service_completed_successfully
env_file: .app.env
entrypoint: [ "php", "bin/console", "messenger:consume", "async", "--time-limit=300", "--memory-limit=512M" ]
deploy:
replicas: 3
volumes:
mysql-data:
files:
theme:
media:
thumbnail:
sitemap:
In your setup you should have always an "init" container which does basic stuff like extension updates, theme compile etc with entrypoint /setup
.
When this container exits, you can start your actual app / worker containers. See docker-compose example above for details.
Variable | Default Value | Description |
---|---|---|
APP_ENV | prod | Environment |
APP_SECRET | (empty) | Can be generated with openssl rand -hex 32 |
INSTANCE_ID | (empty) | Unique Identifier for the Store: Can be generated with openssl rand -hex 32 |
JWT_PRIVATE_KEY | (empty) | Can be generated with shopware-cli project generate-jwt --env |
JWT_PUBLIC_KEY | (empty) | Can be generated with shopware-cli project generate-jwt --env |
LOCK_DSN | flock | DSN for Symfony locking |
APP_URL | (empty) | Where Shopware will be accessible |
DATABASE_HOST | (empty) | Host of MySQL (needed for for checking is MySQL alive) |
DATABASE_PORT | 3306 | Host of MySQL (needed for for checking is MySQL alive) |
BLUE_GREEN_DEPLOYMENT | 0 | This needs super priviledge to create trigger |
DATABASE_URL | (empty) | MySQL credentials as DSN |
DATABASE_SSL_CA | (empty) | Path to SSL CA file (needs to be readable for uid 512) |
DATABASE_SSL_CERT | (empty) | Path to SSL Cert file (needs to be readable for uid 512) |
DATABASE_SSL_KEY | (empty) | Path to SSL Key file (needs to be readable for uid 512) |
DATABASE_SSL_DONT_VERIFY_SERVER_CERT | (empty) | Disables verification of the server certificate (1 disables it) |
MAILER_DSN | null://localhost | Mailer DSN (Admin Configuration overwrites this) |
OPENSEARCH_URL | (empty) | OpenSearch Hosts |
SHOPWARE_ES_ENABLED | 0 | OpenSearch Support Enabled? |
SHOPWARE_ES_INDEXING_ENABLED | 0 | OpenSearch Indexing Enabled? |
SHOPWARE_ES_INDEX_PREFIX | (empty) | OpenSearch Index Prefix |
COMPOSER_HOME | /tmp/composer | Caching for the Plugin Manager |
SHOPWARE_HTTP_CACHE_ENABLED | 1 | Is HTTP Cache enabled? |
SHOPWARE_HTTP_DEFAULT_TTL | 7200 | Default TTL for Http Cache |
MESSENGER_TRANSPORT_DSN | (empty) | DSN for default async queue (example: amqp://guest:guest@localhost:5672/%2f/default |
MESSENGER_TRANSPORT_LOW_PRIORITY_DSN | (empty) | DSN for low priority queue (example: amqp://guest:guest@localhost:5672/%2f/low_prio |
MESSENGER_TRANSPORT_FAILURE_DSN | (empty) | DSN for failed messages queue (example: amqp://guest:guest@localhost:5672/%2f/failure |
INSTALL_LOCALE | en-GB | Default locale for the Shop |
INSTALL_CURRENCY | EUR | Default currency for the Shop |
INSTALL_ADMIN_USERNAME | admin | Default admin username |
INSTALL_ADMIN_PASSWORD | shopware | Default admin password |
PHP_SESSION_COOKIE_LIFETIME | 0 | See PHP FPM documentation |
PHP_SESSION_GC_MAXLIFETIME | 1440 | See PHP FPM documentation |
PHP_SESSION_HANDLER | files | Set to redis for redis session |
PHP_SESSION_SAVE_PATH | (empty) | Set to tcp://redis:6379 for redis session |
PHP_MAX_UPLOAD_SIZE | 128m | See PHP documentation |
PHP_MAX_EXECUTION_TIME | 300 | See PHP documentation |
PHP_MEMORY_LIMIT | 512m | See PHP documentation |
FPM_PM | dynamic | See PHP FPM documentation |
FPM_PM_MAX_CHILDREN | 5 | See PHP FPM documentation |
FPM_PM_START_SERVERS | 2 | See PHP FPM documentation |
FPM_PM_MIN_SPARE_SERVERS | 1 | See PHP FPM documentation |
FPM_PM_MAX_SPARE_SERVERS | 3 | See PHP FPM documentation |
In a very basic setup when all files are stored locally you need 5 volumes:
Usage | Path |
---|---|
invoices/private files | /var/www/html/files |
theme files | /var/www/html/public/theme |
images | /var/www/html/public/media |
image thumbnails | /var/www/html/public/thumbnail |
generated sitemap | /var/www/html/public/sitemap |
It is recommanded to use an external storage provider when possible to store the files. With an external storage provider you won't need any mounts. Refer to official Shopware docs for setup.
Use Redis for sessions
You can set PHP_SESSION_HANDLER
to redis
and PHP_SESSION_SAVE_PATH
to any redis path like tcp://redis:6379
Assets are stored locally, but asset-manifest.json tries to write to external location
Override the filesystem of asset-manifest.json to temporary filesystem:
# config/packages/prod/asset-overwrite.yaml
services:
Shopware\Core\Framework\Plugin\Util\AssetService:
arguments:
- '@shopware.filesystem.asset'
- '@shopware.filesystem.temp'
- '@kernel'
- '@Shopware\Core\Framework\Plugin\KernelPluginLoader\KernelPluginLoader'
- '@Shopware\Core\Framework\Adapter\Cache\CacheInvalidator'
- '@Shopware\Core\Framework\App\Lifecycle\AppLoader'
- '@parameter_bag'