├── .docker ├── db │ └── .gitkeep ├── ca-certificates │ └── .gitkeep ├── .env.db ├── cron.sh ├── env.direct.yml ├── service.db.prod.yml ├── env.gitlab.yml ├── service.db.dev.yml ├── env.dev.yml ├── vol.uploads.yml ├── env.multidomain.yml ├── env.setup.yml ├── build.dev.yml ├── service.scheduler.yml ├── service.solr.yml ├── build.yml ├── service.blackfire.yml ├── service.redis-session.yml ├── service.db.yml ├── service.redis-cache.yml ├── env.prod.yml ├── env.dev.volumes.yml ├── service.adminer.yml ├── service.mailhog.yml ├── Dockerfile └── web │ └── default.conf.template ├── packages └── README.md ├── .gitignore ├── .dockerignore ├── docker-compose.yml ├── .env.prod ├── .env.dev ├── .gitlab-ci.example.yml ├── .surf └── typo3.php ├── composer.json ├── private └── typo3conf │ └── AdditionalConfiguration.php ├── .github └── workflows │ └── build-and-push.yml └── README.md /.docker/db/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.docker/ca-certificates/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.docker/.env.db: -------------------------------------------------------------------------------- 1 | MYSQL_DATABASE=typo3 2 | MYSQL_USER=typo3 3 | MYSQL_PASSWORD=typo3 4 | -------------------------------------------------------------------------------- /.docker/cron.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -eu 3 | 4 | exec busybox crond -f -l 0 -L /dev/stdout -------------------------------------------------------------------------------- /packages/README.md: -------------------------------------------------------------------------------- 1 | # Local packages 2 | 3 | Put your local packages like TYPO3 sitepackage or custom extensions here. -------------------------------------------------------------------------------- /.docker/env.direct.yml: -------------------------------------------------------------------------------- 1 | services: 2 | web: 3 | ports: 4 | - "${WEB_PORT:-80}:80" 5 | 6 | networks: 7 | frontend: -------------------------------------------------------------------------------- /.docker/service.db.prod.yml: -------------------------------------------------------------------------------- 1 | services: 2 | db: 3 | volumes: 4 | - db:/var/lib/mysql 5 | 6 | volumes: 7 | db: 8 | -------------------------------------------------------------------------------- /.docker/env.gitlab.yml: -------------------------------------------------------------------------------- 1 | services: 2 | web: 3 | image: ${CI_REGISTRY_IMAGE}/web 4 | 5 | typo3: 6 | image: ${CI_REGISTRY_IMAGE}/typo3 7 | -------------------------------------------------------------------------------- /.docker/service.db.dev.yml: -------------------------------------------------------------------------------- 1 | services: 2 | db: 3 | ports: 4 | - "${DB_BIND_TO}:3306" 5 | volumes: 6 | - .docker/db:/var/lib/mysql 7 | -------------------------------------------------------------------------------- /.docker/env.dev.yml: -------------------------------------------------------------------------------- 1 | services: 2 | web: 3 | volumes: 4 | - ./:/app 5 | 6 | typo3: 7 | volumes: 8 | - ./:/app 9 | environment: 10 | XDEBUG_MODE: 11 | -------------------------------------------------------------------------------- /.docker/vol.uploads.yml: -------------------------------------------------------------------------------- 1 | services: 2 | web: 3 | volumes: 4 | - uploads:/app/private/uploads 5 | 6 | typo3: 7 | volumes: 8 | - uploads:/app/private/uploads 9 | 10 | volumes: 11 | uploads: -------------------------------------------------------------------------------- /.docker/env.multidomain.yml: -------------------------------------------------------------------------------- 1 | services: 2 | web: 3 | networks: 4 | web-typo3: 5 | aliases: 6 | - ${VHOST} 7 | - 2nd.domain.tld 8 | - 3rd.domain.tld 9 | labels: 10 | traefik.frontend.rule: Host:${VHOST},2nd.domain.tld,3rd.domain.tld 11 | -------------------------------------------------------------------------------- /.docker/env.setup.yml: -------------------------------------------------------------------------------- 1 | services: 2 | typo3: 3 | environment: 4 | TYPO3_INSTALL_ADMIN_USER: 5 | TYPO3_INSTALL_ADMIN_PASSWORD: 6 | TYPO3_INSTALL_DB_DRIVER: 7 | TYPO3_INSTALL_SITE_NAME: 8 | TYPO3_INSTALL_SITE_SETUP_TYPE: 9 | TYPO3_INSTALL_WEB_SERVER_CONFIG: 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.docker/db/* 2 | !/.docker/db/.gitkeep 3 | 4 | .DS_Store 5 | .idea 6 | nbproject 7 | /var/* 8 | !/var/labels 9 | /vendor 10 | 11 | /private/* 12 | !/private/typo3conf 13 | /private/typo3conf/* 14 | !/private/typo3conf/*.php 15 | /private/typo3conf/PackageStates.php 16 | 17 | /public/* 18 | !/public/.htaccess 19 | -------------------------------------------------------------------------------- /.docker/build.dev.yml: -------------------------------------------------------------------------------- 1 | services: 2 | web: 3 | build: 4 | dockerfile: .docker/Dockerfile 5 | target: web-development 6 | args: 7 | TARGET_ENVIRONMENT: development 8 | 9 | typo3: 10 | build: 11 | dockerfile: .docker/Dockerfile 12 | target: php-development 13 | args: 14 | TARGET_ENVIRONMENT: development 15 | -------------------------------------------------------------------------------- /.docker/service.scheduler.yml: -------------------------------------------------------------------------------- 1 | services: 2 | scheduler: 3 | image: ${TYPO3_IMAGE} 4 | entrypoint: cron 5 | depends_on: 6 | - db 7 | #- redis 8 | #- solr 9 | networks: 10 | typo3-db: 11 | #typo3-redis: 12 | #typo3-solr: 13 | env_file: 14 | - .docker/.env.db 15 | environment: 16 | MYSQL_HOST: db 17 | restart: "${RESTART}" -------------------------------------------------------------------------------- /.docker/service.solr.yml: -------------------------------------------------------------------------------- 1 | services: 2 | typo3: 3 | depends_on: 4 | - solr 5 | networks: 6 | typo3-solr: 7 | 8 | solr: 9 | image: ${SOLR_IMAGE} 10 | networks: 11 | typo3-solr: 12 | volumes: 13 | - solr:/opt/solr/server/solr/data 14 | restart: "${RESTART}" 15 | 16 | networks: 17 | typo3-solr: 18 | 19 | volumes: 20 | solr: 21 | 22 | -------------------------------------------------------------------------------- /.docker/build.yml: -------------------------------------------------------------------------------- 1 | services: 2 | web: 3 | build: 4 | context: . 5 | dockerfile: .docker/Dockerfile 6 | target: web-production 7 | args: 8 | TARGET_ENVIRONMENT: production 9 | 10 | typo3: 11 | build: 12 | context: . 13 | dockerfile: .docker/Dockerfile 14 | target: php-production 15 | args: 16 | TARGET_ENVIRONMENT: production 17 | -------------------------------------------------------------------------------- /.docker/service.blackfire.yml: -------------------------------------------------------------------------------- 1 | services: 2 | typo3: 3 | environment: 4 | BLACKFIRE_HOST: 5 | networks: 6 | typo3-blackfire: 7 | 8 | blackfire: 9 | image: blackfire/blackfire 10 | environment: 11 | BLACKFIRE_SERVER_ID: 12 | BLACKFIRE_SERVER_TOKEN: 13 | networks: 14 | typo3-blackfire: 15 | restart: "${RESTART}" 16 | 17 | networks: 18 | typo3-blackfire: 19 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | nbproject 4 | /.docker/* 5 | !/.docker/ca-certificates 6 | !/.docker/web 7 | !/.docker/cron.sh 8 | /.dockerignore 9 | /.env* 10 | /.git* 11 | /.surf 12 | /docker-compose.yml 13 | /private/* 14 | !/private/typo3conf 15 | /private/typo3conf/* 16 | !/private/typo3conf/*.php 17 | /private/typo3conf/PackageStates.php 18 | /public 19 | /README.md 20 | /var/* 21 | !/var/labels 22 | /vendor -------------------------------------------------------------------------------- /.docker/service.redis-session.yml: -------------------------------------------------------------------------------- 1 | x-redis: 2 | &redis 3 | image: redis:alpine 4 | networks: 5 | typo3-redis: 6 | restart: "${RESTART}" 7 | 8 | services: 9 | typo3: 10 | depends_on: 11 | - redis-session-be 12 | - redis-session-fe 13 | environment: 14 | REDIS_SESSION_HOST_PREFIX: redis-session- 15 | networks: 16 | typo3-redis: 17 | 18 | redis-session-be: *redis 19 | redis-session-fe: *redis 20 | 21 | networks: 22 | typo3-redis: 23 | -------------------------------------------------------------------------------- /.docker/service.db.yml: -------------------------------------------------------------------------------- 1 | services: 2 | typo3: 3 | depends_on: 4 | - db 5 | networks: 6 | typo3-db: 7 | env_file: 8 | - .docker/.env.db 9 | environment: 10 | MYSQL_HOST: db 11 | 12 | db: 13 | image: ${DB_IMAGE} 14 | networks: 15 | typo3-db: 16 | command: 17 | - --character-set-server=utf8mb4 18 | - --collation-server=utf8mb4_unicode_ci 19 | environment: 20 | MYSQL_ROOT_PASSWORD: 21 | env_file: 22 | - .docker/.env.db 23 | restart: "${RESTART}" 24 | 25 | networks: 26 | typo3-db: 27 | -------------------------------------------------------------------------------- /.docker/service.redis-cache.yml: -------------------------------------------------------------------------------- 1 | x-redis: 2 | &redis 3 | image: redis:alpine 4 | networks: 5 | typo3-redis: 6 | restart: "${RESTART}" 7 | 8 | services: 9 | typo3: 10 | depends_on: 11 | - redis-cache-extbase 12 | - redis-cache-hash 13 | - redis-cache-imagesizes 14 | - redis-cache-pages 15 | - redis-cache-pagesection 16 | - redis-cache-rootline 17 | environment: 18 | REDIS_CACHE_HOST_PREFIX: redis-cache- 19 | networks: 20 | typo3-redis: 21 | 22 | redis-cache-extbase: *redis 23 | redis-cache-hash: *redis 24 | redis-cache-imagesizes: *redis 25 | redis-cache-pages: *redis 26 | redis-cache-pagesection: *redis 27 | redis-cache-rootline: *redis 28 | 29 | networks: 30 | typo3-redis: 31 | -------------------------------------------------------------------------------- /.docker/env.prod.yml: -------------------------------------------------------------------------------- 1 | services: 2 | web: 3 | volumes: 4 | - type: volume 5 | source: fileadmin 6 | target: /app/private/fileadmin 7 | read_only: true 8 | volume: 9 | nocopy: true 10 | - type: volume 11 | source: typo3temp 12 | target: /app/private/typo3temp 13 | read_only: true 14 | volume: 15 | nocopy: true 16 | 17 | typo3: 18 | volumes: 19 | - fileadmin:/app/private/fileadmin 20 | - typo3temp:/app/private/typo3temp 21 | - var-charset:/app/var/charset 22 | - var-lock:/app/var/lock 23 | - var-log:/app/var/log 24 | - var-session:/app/var/session 25 | 26 | volumes: 27 | fileadmin: 28 | typo3temp: 29 | var-charset: 30 | var-lock: 31 | var-log: 32 | var-session: 33 | -------------------------------------------------------------------------------- /.docker/env.dev.volumes.yml: -------------------------------------------------------------------------------- 1 | services: 2 | web: 3 | volumes: 4 | - type: volume 5 | source: fileadmin 6 | target: /app/private/fileadmin 7 | read_only: true 8 | volume: 9 | nocopy: true 10 | - type: volume 11 | source: typo3temp 12 | target: /app/private/typo3temp 13 | read_only: true 14 | volume: 15 | nocopy: true 16 | 17 | typo3: 18 | volumes: 19 | - fileadmin:/app/private/fileadmin 20 | - ./private/fileadmin/form_definitions:/app/private/fileadmin/form_definitions 21 | - typo3temp:/app/private/typo3temp 22 | - var:/app/var 23 | - ./var/labels:/app/var/labels 24 | - ./var/log:/app/var/log 25 | 26 | db: 27 | volumes: 28 | - db:/var/lib/mysql 29 | 30 | volumes: 31 | db: 32 | fileadmin: 33 | typo3temp: 34 | var: 35 | -------------------------------------------------------------------------------- /.docker/service.adminer.yml: -------------------------------------------------------------------------------- 1 | services: 2 | db: 3 | networks: 4 | adminer-db: 5 | 6 | adminer: 7 | image: adminer 8 | depends_on: 9 | - db 10 | networks: 11 | adminer-db: 12 | frontend: 13 | labels: 14 | - "traefik.enable=true" 15 | - "traefik.docker.network=${FRONTEND_NETWORK:-frontend}" 16 | - "traefik.http.routers.adminer-http-${COMPOSE_PROJECT_NAME}.rule=Host(`adminer-${VHOST}`)" 17 | - "traefik.http.routers.adminer-http-${COMPOSE_PROJECT_NAME}.entrypoints=${TRAEFIK_ENTRYPOINT_HTTP}" 18 | - "traefik.http.routers.adminer-https-${COMPOSE_PROJECT_NAME}.rule=Host(`adminer-${VHOST}`)" 19 | - "traefik.http.routers.adminer-https-${COMPOSE_PROJECT_NAME}.entrypoints=${TRAEFIK_ENTRYPOINT_HTTPS}" 20 | - "traefik.http.routers.adminer-https-${COMPOSE_PROJECT_NAME}.tls=true" 21 | - "traefik.http.services.adminer-${COMPOSE_PROJECT_NAME}.loadbalancer.server.port=8080" 22 | restart: "${RESTART}" 23 | 24 | networks: 25 | adminer-db: 26 | -------------------------------------------------------------------------------- /.docker/service.mailhog.yml: -------------------------------------------------------------------------------- 1 | services: 2 | typo3: 3 | environment: 4 | SMTP_SERVER: mailhog 5 | SMTP_PORT: "1025" 6 | networks: 7 | typo3-mailhog: 8 | 9 | mailhog: 10 | image: mailhog/mailhog 11 | networks: 12 | typo3-mailhog: 13 | frontend: 14 | labels: 15 | - "traefik.enable=true" 16 | - "traefik.docker.network=${FRONTEND_NETWORK:-frontend}" 17 | - "traefik.http.routers.mailhog-http-${COMPOSE_PROJECT_NAME}.rule=Host(`mailhog-${VHOST}`)" 18 | - "traefik.http.routers.mailhog-http-${COMPOSE_PROJECT_NAME}.entrypoints=${TRAEFIK_ENTRYPOINT_HTTP}" 19 | - "traefik.http.routers.mailhog-https-${COMPOSE_PROJECT_NAME}.rule=Host(`mailhog-${VHOST}`)" 20 | - "traefik.http.routers.mailhog-https-${COMPOSE_PROJECT_NAME}.entrypoints=${TRAEFIK_ENTRYPOINT_HTTPS}" 21 | - "traefik.http.routers.mailhog-https-${COMPOSE_PROJECT_NAME}.tls=true" 22 | - "traefik.http.services.mailhog-${COMPOSE_PROJECT_NAME}.loadbalancer.server.port=8025" 23 | restart: "${RESTART}" 24 | 25 | networks: 26 | typo3-mailhog: 27 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | web: 3 | image: ${WEB_IMAGE} 4 | networks: 5 | frontend: 6 | web-typo3: 7 | aliases: 8 | - ${VHOST} 9 | depends_on: 10 | - typo3 11 | labels: 12 | - "traefik.enable=true" 13 | - "traefik.docker.network=${FRONTEND_NETWORK:-frontend}" 14 | - "traefik.http.routers.web-http-${COMPOSE_PROJECT_NAME}.rule=Host(`${VHOST}`)" 15 | - "traefik.http.routers.web-http-${COMPOSE_PROJECT_NAME}.entrypoints=${TRAEFIK_ENTRYPOINT_HTTP}" 16 | - "traefik.http.routers.web-https-${COMPOSE_PROJECT_NAME}.rule=Host(`${VHOST}`)" 17 | - "traefik.http.routers.web-https-${COMPOSE_PROJECT_NAME}.entrypoints=${TRAEFIK_ENTRYPOINT_HTTPS}" 18 | - "traefik.http.routers.web-https-${COMPOSE_PROJECT_NAME}.tls=true" 19 | - "traefik.http.services.web-${COMPOSE_PROJECT_NAME}.loadbalancer.server.port=80" 20 | restart: "${RESTART}" 21 | 22 | typo3: 23 | image: ${TYPO3_IMAGE} 24 | networks: 25 | web-typo3: 26 | environment: 27 | TRUSTED_HOSTS_PATTERN: 28 | restart: "${RESTART}" 29 | 30 | networks: 31 | web-typo3: 32 | frontend: 33 | external: true 34 | name: ${FRONTEND_NETWORK:-frontend} 35 | -------------------------------------------------------------------------------- /.env.prod: -------------------------------------------------------------------------------- 1 | # Set a unique project name 2 | # E.g. project-typo3version like company-typo3v11 3 | COMPOSE_PROJECT_NAME=docker-typo3v11-dev 4 | 5 | # Specify the path to Compose file(s) 6 | # https://docs.docker.com/compose/reference/envvars/#compose_file 7 | # You must start with the root docker-compose.yml followed by services, environment and build config if necessary. 8 | COMPOSE_FILE=docker-compose.yml:.docker/service.db.yml:.docker/service.db.prod.yml:.docker/service.redis-cache.yml:.docker/service.redis-session.yml:.docker/service.solr.yml:.docker/env.prod.yml:.docker/build.yml 9 | 10 | VHOST=www.domain.tld 11 | ADDITIONAL_VHOSTS= 12 | TRUSTED_HOSTS_PATTERN=www\.domain\.tld 13 | FRONTEND_NETWORK=frontend 14 | 15 | # https://docs.docker.com/compose/compose-file/#restart 16 | RESTART=always 17 | 18 | TYPO3_IMAGE=vendor/project:typo3 19 | WEB_IMAGE=vendor/project:web 20 | 21 | # Database related settings 22 | DB_IMAGE=mariadb:10.6 23 | # MYSQL_ROOT_PASSWORD should be passed by environment/CI variable 24 | 25 | SOLR_IMAGE=typo3solr/ext-solr:9.0.0 26 | 27 | #BLACKFIRE_HOST=blackfire 28 | #BLACKFIRE_SERVER_ID= 29 | #BLACKFIRE_SERVER_TOKEN= 30 | 31 | # Configure Traefik 32 | TRAEFIK_ENTRYPOINT_HTTP=http 33 | TRAEFIK_ENTRYPOINT_HTTPS=https 34 | -------------------------------------------------------------------------------- /.env.dev: -------------------------------------------------------------------------------- 1 | # Set a unique project name 2 | # E.g. project-typo3version like company-typo3v11 3 | COMPOSE_PROJECT_NAME=docker-typo3v11-dev 4 | 5 | # Specify the path to Compose file(s) 6 | # https://docs.docker.com/compose/reference/envvars/#compose_file 7 | # You must start with the root docker-compose.yml followed by services, environment and build config if necessary. 8 | COMPOSE_FILE=docker-compose.yml:.docker/service.db.yml:.docker/service.db.dev.yml:.docker/service.adminer.yml:.docker/service.mailhog.yml:.docker/service.redis-cache.yml:.docker/service.redis-session.yml:.docker/service.solr.yml:.docker/service.blackfire.yml:.docker/env.dev.yml 9 | 10 | VHOST=typo3.localhost 11 | ADDITIONAL_VHOSTS= 12 | TRUSTED_HOSTS_PATTERN=.* 13 | FRONTEND_NETWORK=frontend 14 | 15 | # https://docs.docker.com/compose/compose-file/#restart 16 | RESTART=no 17 | 18 | TYPO3_IMAGE=ghcr.io/t3easy/typo3:typo3-dev-master-dev 19 | WEB_IMAGE=ghcr.io/t3easy/typo3:web-dev-master-dev 20 | 21 | # The port the web container expose. Only if you use `.docker/env.direct.yml`. 22 | WEB_PORT=80 23 | 24 | # Database related settings 25 | DB_IMAGE=mariadb:10.6 26 | DB_BIND_TO=127.0.0.1: 27 | #DB_BIND_TO=127.0.0.1:13306 28 | MYSQL_ROOT_PASSWORD=typo3 29 | 30 | SOLR_IMAGE=typo3solr/ext-solr:9.0.0 31 | 32 | BLACKFIRE_HOST=blackfire 33 | BLACKFIRE_SERVER_ID= 34 | BLACKFIRE_SERVER_TOKEN= 35 | 36 | # https://xdebug.org/docs/all_settings#mode 37 | XDEBUG_MODE=off 38 | 39 | # Configure Traefik 40 | TRAEFIK_ENTRYPOINT_HTTP=http 41 | TRAEFIK_ENTRYPOINT_HTTPS=https 42 | -------------------------------------------------------------------------------- /.gitlab-ci.example.yml: -------------------------------------------------------------------------------- 1 | image: docker:stable 2 | 3 | variables: 4 | DOCKER_TLS_VERIFY: "1" 5 | DOCKER_CERT_PATH: ".docker-cert" 6 | 7 | before_script: 8 | - apk add --no-cache py-pip 9 | - pip install --no-cache-dir docker-compose 10 | - mkdir -p $DOCKER_CERT_PATH 11 | - echo "$DOCKER_CA" > $DOCKER_CERT_PATH/ca.pem 12 | - echo "$DOCKER_CERT" > $DOCKER_CERT_PATH/cert.pem 13 | - echo "$DOCKER_KEY" > $DOCKER_CERT_PATH/key.pem 14 | - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY 15 | 16 | after_script: 17 | - docker logout $CI_REGISTRY 18 | - rm -rf $DOCKER_CERT_PATH 19 | 20 | .dedicated-runner: &dedicated-runner 21 | tags: 22 | - docker 23 | - linux 24 | 25 | build: 26 | <<: *dedicated-runner 27 | stage: build 28 | variables: 29 | DOCKER_HOST: "tcp://docker-host:2376" 30 | COMPOSE_FILE: ".docker/build.yml:.docker/env.gitlab.yml" 31 | script: 32 | - docker-compose build --pull 33 | - docker-compose push 34 | 35 | deploy:live: 36 | <<: *dedicated-runner 37 | stage: deploy 38 | variables: 39 | DOCKER_HOST: "tcp://docker-host:2376" 40 | COMPOSE_FILE: "docker-compose.yml:\ 41 | .docker/service.redis-cache.yml:\ 42 | .docker/service.redis-session.yml:\ 43 | .docker/env.prod.yml:\ 44 | .docker/env.gitlab.yml" 45 | VHOST: "www.domain.tld" 46 | FRONTEND_NETWORK: "frontend" 47 | RESTART: "always" 48 | DB_IMAGE: "mariadb:10.2" 49 | script: 50 | - docker-compose pull 51 | - docker-compose up -d 52 | - docker-compose exec -u www-data typo3 typo3cms extension:setupactive 53 | - docker-compose exec -u www-data typo3 typo3cms cache:flush --force 54 | -------------------------------------------------------------------------------- /.docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | ARG PHP_VERSION=7.4 3 | ARG TARGET_ENVIRONMENT=production 4 | ARG NODE_VERSION=14 5 | 6 | FROM node:${NODE_VERSION}-alpine as node 7 | ENV YARN_CACHE_FOLDER /tmp/cache/yarn 8 | ENV npm_config_cache /tmp/cache/npm 9 | 10 | RUN set -eux; \ 11 | yarn global add gulp-cli; \ 12 | rm -rf ${YARN_CACHE_FOLDER}; \ 13 | gulp --version 14 | WORKDIR /app 15 | 16 | FROM nginx:alpine as web-base 17 | LABEL org.opencontainers.image.source="https://github.com/t3easy/docker-typo3" 18 | ENV CLIENT_MAX_BODY_SIZE=100m 19 | COPY .docker/web/*.conf.template /etc/nginx/templates/ 20 | WORKDIR /app 21 | 22 | FROM web-base as web-development 23 | 24 | FROM ghcr.io/t3easy/php:${PHP_VERSION}-${TARGET_ENVIRONMENT} AS php-base 25 | LABEL org.opencontainers.image.source="https://github.com/t3easy/docker-typo3" 26 | COPY .docker/cron.sh /usr/local/bin/cron 27 | RUN rm /var/spool/cron/crontabs/root \ 28 | && echo '*/15 * * * * php -f /app/vendor/bin/typo3 scheduler:run' > /var/spool/cron/crontabs/www-data 29 | COPY .docker/ca-certificates /usr/local/share/ca-certificates 30 | RUN update-ca-certificates 31 | RUN set -eux; \ 32 | mkdir -p /app/config; \ 33 | mkdir -p /app/private/fileadmin; \ 34 | mkdir -p /app/private/typo3temp; \ 35 | mkdir -p /app/var; \ 36 | mkdir -p /app/var/session; \ 37 | chown -R www-data:www-data /app 38 | 39 | FROM php-base as php-development 40 | ENV TYPO3_CONTEXT=Development 41 | 42 | FROM ghcr.io/t3easy/php:${PHP_VERSION}-development AS builder 43 | ENV XDEBUG_MODE=off 44 | COPY . /app 45 | # Remove .docker because it cannot be excluded with .dockerignore 46 | RUN rm -rf /app/.docker 47 | RUN set -eux; \ 48 | composer install --no-ansi --no-interaction --no-dev --no-progress --classmap-authoritative; \ 49 | typo3 cache:warmup 50 | 51 | FROM web-base as web-production 52 | COPY --chown=101 --from=builder /app /app 53 | 54 | FROM php-base as php-production 55 | COPY --chown=82 --from=builder /app . 56 | ENV TYPO3_CONTEXT=Production 57 | -------------------------------------------------------------------------------- /.surf/typo3.php: -------------------------------------------------------------------------------- 1 | setHostname($node->getName()) 7 | ->setOption('username', 'user') 8 | ->setOption('phpBinaryPathAndFilename', '/usr/local/bin/php_cli'); 9 | 10 | $application = new \TYPO3\Surf\Application\TYPO3\CMS(); 11 | $application 12 | ->setDeploymentPath('/httpdocs') 13 | ->setOption('baseUrl', 'https://my.node.com/') 14 | ->setOption('webDirectory', 'public') 15 | ->setOption('symlinkDataFolders', ['fileadmin']) 16 | ->setOption('repositoryUrl', 'file://' . dirname(__DIR__)) 17 | ->setOption('keepReleases', 3) 18 | ->setOption('composerCommandPath', 'composer') 19 | ->setOption('rsyncExcludes', [ 20 | '.docker*', 21 | '.editorconfig', 22 | '.env*', 23 | '.git*', 24 | '.surf', 25 | 'docker-compose.yml', 26 | 'public/fileadmin', 27 | 'README.md' 28 | ]) 29 | ->setOption('scriptBasePath', \TYPO3\Flow\Utility\Files::concatenatePaths([$deployment->getWorkspacePath($application), $application->getOption('webDirectory')])) 30 | ->addSymlink($application->getOption('webDirectory') . '/typo3conf/LocalConfiguration.php', '../../../../shared/Configuration/LocalConfiguration.php') 31 | ->addNode($node); 32 | 33 | $deployment 34 | ->addApplication($application) 35 | ->onInitialize( 36 | function () use ($deployment, $application) { 37 | $deployment->getWorkflow() 38 | ->beforeTask(\TYPO3\Surf\Task\TYPO3\CMS\SetUpExtensionsTask::class, \TYPO3\Surf\Task\TYPO3\CMS\CompareDatabaseTask::class, $application) 39 | ->beforeStage('transfer', \TYPO3\Surf\Task\Php\WebOpcacheResetCreateScriptTask::class, $application) 40 | ->afterStage('switch', \TYPO3\Surf\Task\Php\WebOpcacheResetExecuteTask::class, $application) 41 | // CreatePackageStatesTask is done by post-autoload-dump script and can be removed 42 | // https://github.com/TYPO3/TYPO3.CMS.BaseDistribution/blob/9.x/composer.json#L38 43 | ->removeTask(\TYPO3\Surf\Task\TYPO3\CMS\CreatePackageStatesTask::class, $application) 44 | ->removeTask(\TYPO3\Surf\Task\TYPO3\CMS\CopyConfigurationTask::class, $application); 45 | } 46 | ); -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "t3easy/typo3-bootcamp", 3 | "description": "Environment to develop and run TYPO3 in Docker containers", 4 | "license": "GPL-2.0-or-later", 5 | "type": "project", 6 | "authors": [ 7 | { 8 | "name": "Jan Kiesewetter", 9 | "email": "jan@t3easy.de" 10 | } 11 | ], 12 | "require": { 13 | "helhum/typo3-console": "^7.1.2", 14 | "helhum/typo3-secure-web": "^0.3.2", 15 | "typo3/cms-backend": "11.5.x-dev", 16 | "typo3/cms-belog": "11.5.x-dev", 17 | "typo3/cms-beuser": "11.5.x-dev", 18 | "typo3/cms-core": "11.5.x-dev", 19 | "typo3/cms-dashboard": "11.5.x-dev", 20 | "typo3/cms-extbase": "11.5.x-dev", 21 | "typo3/cms-extensionmanager": "11.5.x-dev", 22 | "typo3/cms-felogin": "11.5.x-dev", 23 | "typo3/cms-filelist": "11.5.x-dev", 24 | "typo3/cms-fluid": "11.5.x-dev", 25 | "typo3/cms-fluid-styled-content": "11.5.x-dev", 26 | "typo3/cms-form": "11.5.x-dev", 27 | "typo3/cms-frontend": "11.5.x-dev", 28 | "typo3/cms-impexp": "11.5.x-dev", 29 | "typo3/cms-info": "11.5.x-dev", 30 | "typo3/cms-install": "11.5.x-dev", 31 | "typo3/cms-recordlist": "11.5.x-dev", 32 | "typo3/cms-redirects": "11.5.x-dev", 33 | "typo3/cms-rte-ckeditor": "11.5.x-dev", 34 | "typo3/cms-scheduler": "11.5.x-dev", 35 | "typo3/cms-seo": "11.5.x-dev", 36 | "typo3/cms-setup": "11.5.x-dev", 37 | "typo3/cms-sys-note": "11.5.x-dev", 38 | "typo3/cms-t3editor": "11.5.x-dev", 39 | "typo3/cms-tstemplate": "11.5.x-dev", 40 | "typo3/cms-viewpage": "11.5.x-dev" 41 | }, 42 | "require-dev": { 43 | "typo3/cms-adminpanel": "11.5.x-dev", 44 | "typo3/cms-lowlevel": "11.5.x-dev", 45 | "typo3/cms-reports": "11.5.x-dev" 46 | }, 47 | "repositories": { 48 | "local": { 49 | "type": "path", 50 | "url": "packages/*", 51 | "canonical": false, 52 | "options": { 53 | "reference": "none" 54 | } 55 | } 56 | }, 57 | "config": { 58 | "allow-plugins": { 59 | "helhum/typo3-console-plugin": true, 60 | "typo3/class-alias-loader": true, 61 | "typo3/cms-composer-installers": true 62 | }, 63 | "platform": { 64 | "php": "7.4.30" 65 | }, 66 | "sort-packages": true 67 | }, 68 | "extra": { 69 | "typo3/cms": { 70 | "root-dir": "private", 71 | "web-dir": "public" 72 | } 73 | }, 74 | "scripts": { 75 | "post-autoload-dump": [ 76 | "@typo3-cms-scripts" 77 | ], 78 | "typo3-cms-scripts": [ 79 | "typo3cms install:fixfolderstructure" 80 | ] 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /private/typo3conf/AdditionalConfiguration.php: -------------------------------------------------------------------------------- 1 | 'utf8mb4', 12 | 'dbname' => getenv('MYSQL_DATABASE'), 13 | 'driver' => 'mysqli', 14 | 'host' => getenv('MYSQL_HOST'), 15 | 'password' => getenv('MYSQL_PASSWORD'), 16 | 'port' => 3306, 17 | 'tableoptions' => [ 18 | 'charset' => 'utf8mb4', 19 | 'collate' => 'utf8mb4_unicode_ci', 20 | ], 21 | 'user' => getenv('MYSQL_USER'), 22 | ]; 23 | if ($mysql['dbname'] && $mysql['host'] && $mysql['password'] && $mysql['user']) { 24 | $GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default'] = $mysql; 25 | } 26 | 27 | if (($smtpServer = getenv('SMTP_SERVER')) && ($smtpPort = getenv('SMTP_PORT'))) { 28 | $GLOBALS['TYPO3_CONF_VARS']['MAIL']['transport'] = 'smtp'; 29 | $GLOBALS['TYPO3_CONF_VARS']['MAIL']['transport_smtp_server'] = $smtpServer . ':' . $smtpPort; 30 | } 31 | 32 | $oneDay = 86400; 33 | $caches = [ 34 | 'extbase' => 0, 35 | 'hash' => 1 * $oneDay, 36 | 'imagesizes' => 0, 37 | 'pages' => 1 * $oneDay, 38 | 'pagesection' => 1 * $oneDay, 39 | 'rootline' => 1 * $oneDay 40 | ]; 41 | 42 | $redisDatabase = 3; 43 | $redisPort = 6379; 44 | 45 | if ($redisCacheHostPrefix = getenv('REDIS_CACHE_HOST_PREFIX')) { 46 | foreach ($caches as $cache => $defaultLifetime) { 47 | $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations'][$cache]['backend'] = \TYPO3\CMS\Core\Cache\Backend\RedisBackend::class; 48 | $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations'][$cache]['options'] = [ 49 | 'database' => $redisDatabase, 50 | 'hostname' => $redisCacheHostPrefix . str_replace('_', '-', $cache), 51 | 'port' => $redisPort, 52 | 'defaultLifetime' => $defaultLifetime 53 | ]; 54 | } 55 | } elseif (extension_loaded('apcu') && ini_get('apc.enabled')) { 56 | foreach ($caches as $cache => $defaultLifetime) { 57 | $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations'][$cache]['backend'] = \TYPO3\CMS\Core\Cache\Backend\ApcuBackend::class; 58 | $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations'][$cache]['options'] = [ 59 | 'defaultLifetime' => $defaultLifetime, 60 | ]; 61 | } 62 | } 63 | 64 | if ($redisSessionHostPrefix = getenv('REDIS_SESSION_HOST_PREFIX')) { 65 | $GLOBALS['TYPO3_CONF_VARS']['SYS']['session'] = [ 66 | 'BE' => [ 67 | 'backend' => \TYPO3\CMS\Core\Session\Backend\RedisSessionBackend::class, 68 | 'options' => [ 69 | 'hostname' => $redisSessionHostPrefix . 'be', 70 | 'database' => $redisDatabase, 71 | 'port' => $redisPort, 72 | ], 73 | ], 74 | 'FE' => [ 75 | 'backend' => \TYPO3\CMS\Core\Session\Backend\RedisSessionBackend::class, 76 | 'options' => [ 77 | 'hostname' => $redisSessionHostPrefix . 'fe', 78 | 'database' => $redisDatabase, 79 | 'port' => $redisPort, 80 | ], 81 | ], 82 | ]; 83 | } 84 | 85 | $GLOBALS['TYPO3_CONF_VARS']['BE']['versionNumberInFilename'] = 'embed'; 86 | $GLOBALS['TYPO3_CONF_VARS']['FE']['versionNumberInFilename'] = 'embed'; 87 | 88 | if ($isDocker) { 89 | $GLOBALS['TYPO3_CONF_VARS']['GFX'] = array_merge( 90 | $GLOBALS['TYPO3_CONF_VARS']['GFX'], 91 | [ 92 | 'processor' => 'GraphicsMagick', 93 | 'processor_allowTemporaryMasksAsPng' => false, 94 | 'processor_colorspace' => 'RGB', 95 | 'processor_effects' => false, 96 | 'processor_enabled' => true, 97 | 'processor_path' => '/usr/bin/', 98 | 'processor_path_lzw' => '/usr/bin/', 99 | ] 100 | ); 101 | $GLOBALS['TYPO3_CONF_VARS']['LOG']['writerConfiguration'] = [ 102 | \TYPO3\CMS\Core\Log\LogLevel::WARNING => [ 103 | \TYPO3\CMS\Core\Log\Writer\PhpErrorLogWriter::class => [] 104 | ] 105 | ]; 106 | unset($GLOBALS['TYPO3_CONF_VARS']['LOG']['TYPO3']['CMS']['Core']['Resource']['ResourceStorage']['writerConfiguration'][\TYPO3\CMS\Core\Log\LogLevel::ERROR][\TYPO3\CMS\Core\Log\Writer\FileWriter::class]); 107 | $GLOBALS['TYPO3_CONF_VARS']['LOG']['TYPO3']['CMS']['Core']['Resource']['ResourceStorage']['writerConfiguration'][\TYPO3\CMS\Core\Log\LogLevel::ERROR][\TYPO3\CMS\Core\Log\Writer\PhpErrorLogWriter::class] = []; 108 | } 109 | 110 | if ($context->isDevelopment()) { 111 | $GLOBALS['TYPO3_CONF_VARS']['LOG']['TYPO3']['CMS']['deprecations']['writerConfiguration'][\TYPO3\CMS\Core\Log\LogLevel::NOTICE][\TYPO3\CMS\Core\Log\Writer\FileWriter::class]['disabled'] = false; 112 | } 113 | 114 | if ($context->isTesting()){ 115 | foreach ($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations'] as $cacheName => $cacheConfiguration) { 116 | $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations'][$cacheName]['backend'] = \TYPO3\CMS\Core\Cache\Backend\NullBackend::class; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /.github/workflows/build-and-push.yml: -------------------------------------------------------------------------------- 1 | name: Build and push Docker images 2 | 3 | on: 4 | schedule: 5 | - cron: '42 5 * * *' 6 | push: 7 | branches: 8 | - '**' 9 | tags: 10 | - 'v*.*.*' 11 | pull_request: 12 | branches: 13 | - '**' 14 | 15 | jobs: 16 | build-and-push: 17 | name: Build and push Docker image 18 | runs-on: ubuntu-latest 19 | permissions: 20 | contents: read 21 | packages: write 22 | strategy: 23 | matrix: 24 | include: 25 | - branch: main 26 | typo3-version: dev-master 27 | - branch: 12.x 28 | typo3-version: 12 29 | - branch: 11.x 30 | typo3-version: 11 31 | - branch: 10.x 32 | typo3-version: 10 33 | - branch: 9.x 34 | typo3-version: 9 35 | - branch: main 36 | node-version: 16 37 | - branch: main 38 | node-version: 14 39 | - branch: main 40 | node-version: 12 41 | - branch: main 42 | node-version: 10 43 | 44 | env: 45 | IMAGE_NAME: t3easy/typo3 46 | PLATFORMS: linux/amd64,linux/arm64 47 | steps: 48 | - name: Checkout 49 | uses: actions/checkout@v3 50 | with: 51 | ref: ${{ matrix.branch }} 52 | 53 | - name: Set up QEMU 54 | uses: docker/setup-qemu-action@v2 55 | 56 | - name: Set up Docker Buildx 57 | uses: docker/setup-buildx-action@v2 58 | 59 | - name: Cache Docker layers 60 | uses: actions/cache@v3 61 | with: 62 | path: /tmp/.buildx-cache 63 | key: ${{ runner.os }}-buildx-${{ github.sha }} 64 | restore-keys: | 65 | ${{ runner.os }}-buildx- 66 | 67 | - name: Login to DockerHub 68 | uses: docker/login-action@v2 69 | if: github.event_name != 'pull_request' 70 | with: 71 | username: ${{ secrets.DOCKERHUB_USERNAME }} 72 | password: ${{ secrets.DOCKERHUB_TOKEN }} 73 | 74 | - name: Login to GitHub Container Registry 75 | if: github.event_name != 'pull_request' 76 | uses: docker/login-action@v2 77 | with: 78 | registry: ghcr.io 79 | username: ${{ github.repository_owner }} 80 | password: ${{ secrets.GITHUB_TOKEN }} 81 | 82 | - name: Build and push web-development 83 | uses: docker/build-push-action@v3 84 | if: ${{ matrix.typo3-version }} 85 | with: 86 | build-args: | 87 | TARGET_ENVIRONMENT=development 88 | context: . 89 | file: .docker/Dockerfile 90 | tags: | 91 | ${{ env.IMAGE_NAME }}:web-${{ matrix.typo3-version }}-dev 92 | ghcr.io/${{ env.IMAGE_NAME }}:web-${{ matrix.typo3-version }}-dev 93 | target: web-development 94 | platforms: ${{ env.PLATFORMS }} 95 | push: ${{ github.event_name != 'pull_request' }} 96 | cache-from: type=local,src=/tmp/.buildx-cache 97 | cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max 98 | 99 | - name: Build and push php-development 100 | uses: docker/build-push-action@v3 101 | if: ${{ matrix.typo3-version }} 102 | with: 103 | build-args: | 104 | TARGET_ENVIRONMENT=development 105 | context: . 106 | file: .docker/Dockerfile 107 | tags: | 108 | ${{ env.IMAGE_NAME }}:typo3-${{ matrix.typo3-version }}-dev 109 | ghcr.io/${{ env.IMAGE_NAME }}:typo3-${{ matrix.typo3-version }}-dev 110 | target: php-development 111 | platforms: ${{ env.PLATFORMS }} 112 | push: ${{ github.event_name != 'pull_request' }} 113 | cache-from: type=local,src=/tmp/.buildx-cache 114 | cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max 115 | 116 | - name: Build and push web-production 117 | uses: docker/build-push-action@v3 118 | if: ${{ matrix.typo3-version }} 119 | with: 120 | context: . 121 | file: .docker/Dockerfile 122 | tags: | 123 | ${{ env.IMAGE_NAME }}:web-${{ matrix.typo3-version }} 124 | ghcr.io/${{ env.IMAGE_NAME }}:web-${{ matrix.typo3-version }} 125 | target: web-production 126 | platforms: ${{ env.PLATFORMS }} 127 | push: ${{ github.event_name != 'pull_request' }} 128 | cache-from: type=local,src=/tmp/.buildx-cache 129 | cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max 130 | 131 | - name: Build and push php-production 132 | uses: docker/build-push-action@v3 133 | if: ${{ matrix.typo3-version }} 134 | with: 135 | context: . 136 | file: .docker/Dockerfile 137 | tags: | 138 | ${{ env.IMAGE_NAME }}:typo3-${{ matrix.typo3-version }} 139 | ghcr.io/${{ env.IMAGE_NAME }}:typo3-${{ matrix.typo3-version }} 140 | target: php-production 141 | platforms: ${{ env.PLATFORMS }} 142 | push: ${{ github.event_name != 'pull_request' }} 143 | cache-from: type=local,src=/tmp/.buildx-cache 144 | cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max 145 | 146 | - name: Build and push node 147 | uses: docker/build-push-action@v3 148 | if: ${{ matrix.node-version }} 149 | with: 150 | context: . 151 | file: .docker/Dockerfile 152 | tags: | 153 | ${{ env.IMAGE_NAME }}:node-${{ matrix.node-version }} 154 | ghcr.io/${{ env.IMAGE_NAME }}:node-${{ matrix.node-version }} 155 | target: node 156 | platforms: ${{ env.PLATFORMS }} 157 | push: ${{ github.event_name != 'pull_request' }} 158 | cache-from: type=local,src=/tmp/.buildx-cache 159 | cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max 160 | 161 | - name: Move cache 162 | run: | 163 | rm -rf /tmp/.buildx-cache 164 | mv /tmp/.buildx-cache-new /tmp/.buildx-cache 165 | -------------------------------------------------------------------------------- /.docker/web/default.conf.template: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | server_name _; 4 | root /app/public; 5 | client_max_body_size $CLIENT_MAX_BODY_SIZE; 6 | index index.php index.html index.htm; 7 | server_tokens off; 8 | charset utf-8; 9 | 10 | gzip on; 11 | gzip_disable "msie6"; 12 | gzip_vary on; 13 | gzip_comp_level 6; 14 | gzip_types application/atom+xml 15 | application/javascript 16 | application/json 17 | application/ld+json 18 | application/manifest+json 19 | application/rdf+xml 20 | application/rss+xml 21 | application/schema+json 22 | application/vnd.geo+json 23 | application/vnd.ms-fontobject 24 | application/x-font-ttf 25 | application/x-javascript 26 | application/x-web-app-manifest+json 27 | application/xhtml+xml 28 | application/xml 29 | font/eot 30 | font/opentype 31 | image/bmp 32 | image/svg+xml 33 | image/vnd.microsoft.icon 34 | image/x-icon 35 | text/cache-manifest 36 | text/css 37 | text/javascript 38 | text/plain 39 | text/vcard 40 | text/vnd.rim.location.xloc 41 | text/vtt 42 | text/x-component 43 | text/x-cross-domain-policy 44 | text/xml; 45 | 46 | # TYPO3 - Block access to composer files 47 | location ~* composer\.(?:json|lock) { 48 | deny all; 49 | } 50 | 51 | # TYPO3 - Block access to flexform files 52 | location ~* flexform[^.]*\.xml { 53 | deny all; 54 | } 55 | 56 | # TYPO3 - Block access to language files 57 | location ~* locallang[^.]*\.(?:xml|xlf)$ { 58 | deny all; 59 | } 60 | 61 | # TYPO3 - Block access to static typoscript files 62 | location ~* ext_conf_template\.txt|ext_typoscript_constants\.txt|ext_typoscript_setup\.txt { 63 | deny all; 64 | } 65 | 66 | # TYPO3 - Block access to miscellaneous protected files 67 | location ~* /.*\.(?:bak|co?nf|cfg|ya?ml|ts|typoscript|tsconfig|dist|fla|in[ci]|log|sh|sql|sqlite)$ { 68 | deny all; 69 | } 70 | 71 | # TYPO3 - Block access to recycler and temporary directories 72 | location ~ _(?:recycler|temp)_/ { 73 | deny all; 74 | } 75 | 76 | # TYPO3 - Block access to configuration files stored in fileadmin 77 | location ~ ^/fileadmin/(?:templates)/.*\.(?:txt|ts)$ { 78 | deny all; 79 | } 80 | 81 | # TYPO3 - Block access to libraries, source and temporary compiled data 82 | location ~ ^/(?:vendor|typo3_src|typo3temp/var) { 83 | deny all; 84 | } 85 | 86 | # TYPO3 - Block access to protected extension directories 87 | location ~ (?:typo3conf/ext|typo3/sysext|typo3/ext)/[^/]+/(?:Configuration|Resources/Private|Tests?|Documentation|docs?)/ { 88 | deny all; 89 | } 90 | 91 | # Compressing resource files will save bandwidth and so improve loading speed especially for users 92 | # with slower internet connections. TYPO3 can compress the .js and .css files for you. 93 | # *) Set $GLOBALS['TYPO3_CONF_VARS']['BE']['compressionLevel'] = 9 for the Backend 94 | # *) Set $GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel'] = 9 together with the TypoScript properties 95 | # config.compressJs and config.compressCss for GZIP compression of Frontend JS and CSS files. 96 | location ~ \.js\.gzip$ { 97 | add_header Content-Encoding gzip; 98 | gzip off; 99 | types { text/javascript gzip; } 100 | } 101 | location ~ \.css\.gzip$ { 102 | add_header Content-Encoding gzip; 103 | gzip off; 104 | types { text/css gzip; } 105 | } 106 | 107 | # TYPO3 - Rule for versioned static files, configured through: 108 | # - $GLOBALS['TYPO3_CONF_VARS']['BE']['versionNumberInFilename'] 109 | # - $GLOBALS['TYPO3_CONF_VARS']['FE']['versionNumberInFilename'] 110 | location ~ ^(.+)\.(\d+)\.(php|js|css|png|jpg|gif|gzip)$ { 111 | try_files $uri $1.$3 =404; 112 | expires max; 113 | } 114 | 115 | # TYPO3 - Static File Directories 116 | location ~ ^/(?:typo3conf|typo3temp)/ { 117 | # Do nothing 118 | } 119 | location ~ ^/(?:fileadmin|uploads)/.*\.pdf$ { 120 | add_header Content-Security-Policy "default-src 'self' 'unsafe-inline'; script-src 'none'; object-src 'self'; plugin-types application/pdf;"; 121 | } 122 | location ~ ^/(?:fileadmin|uploads)/ { 123 | add_header Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'none'; object-src 'none';"; 124 | } 125 | 126 | location ~* \.(?:js|css|png|jpg|jpeg|gif|ico|svg)$ { 127 | try_files $uri /index.php$is_args$args; 128 | expires max; 129 | } 130 | 131 | # TYPO3 - Rewrite "/typo3" without trailing slash 132 | location = /typo3 { 133 | rewrite ^ /typo3/; 134 | } 135 | 136 | # TYPO3 - If the file/directory does not exist but is below /typo3/, redirect to the TYPO3 Backend entry point. 137 | location /typo3/ { 138 | try_files $uri /typo3/index.php$is_args$args; 139 | } 140 | 141 | # TYPO3 - If the file/directory does not exist => Redirect to index.php. 142 | location / { 143 | try_files $uri $uri/ /index.php$is_args$args; 144 | } 145 | 146 | location ~ [^/]\.php(/|$) { 147 | fastcgi_split_path_info ^(.+?\.php)(/.*)$; 148 | if (!-f $document_root$fastcgi_script_name) { 149 | return 404; 150 | } 151 | 152 | # Mitigate https://httpoxy.org/ vulnerabilities 153 | fastcgi_param HTTP_PROXY ""; 154 | 155 | fastcgi_buffer_size 32k; 156 | fastcgi_buffers 8 16k; 157 | fastcgi_connect_timeout 240s; 158 | fastcgi_read_timeout 240s; 159 | fastcgi_send_timeout 240s; 160 | fastcgi_pass typo3:9000; 161 | fastcgi_index index.php; 162 | include fastcgi.conf; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TYPO3 Bootcamp - an Environment to develop and run TYPO3 in Docker containers 2 | 3 | ### Requirements: 4 | 1. Docker(4Mac) 17.09 or greater for build 5 | 1. [Docker Compose](https://docs.docker.com/compose/install/) (included in Docker4Mac) 6 | 1. composer 7 | 1. A Traefik reverse proxy, e.g. [docker-frontend](https://github.com/t3easy/docker-frontend) 8 | or include `.docker/env.direct.yml` and set `WEB_PORT`. 9 | 10 | ### Start a new project 11 | 1. `composer create-project t3easy/typo3-bootcamp awesome-project.tld` 12 | (Or clone the project with git and checkout the desired branch) 13 | 1. Change to awesome-project.tld / open it in you favorite IDE 14 | 1. Rename `.env.dev` to `.env` and adjust it to your needs, see below and comments in the file for more information 15 | E.g. `VHOST=typo3.localhost` 16 | If you use a `.localhost` vhost, you can access it with Chrome w/o a host entry. 17 | See: 18 | 1. Add your vhost as a hosts entry for 127.0.0.1 / the box you're running docker on 19 | 1. Start the environment with `docker-compose up -d` 20 | 1. Setup TYPO3 21 | 1. With TYPO3 Console 22 | ```bash 23 | docker-compose exec -u www-data typo3 vendor/bin/typo3cms install:setup 24 | ``` 25 | 1. Or with the browser 26 | ```bash 27 | docker-compose exec -u www-data typo3 touch /app/private/FIRST_INSTALL 28 | ``` 29 | Open and configure TYPO3 30 | 1. Go to 31 | * for TYPO3 frontend 32 | * for TYPO3 backend 33 | * for Adminer 34 | * for Mailhog 35 | 36 | ## .env 37 | In this file you define the environment you'd like to setup. 38 | There are two examples, `.env.dev` to start an development environment and `.env.prod` as a template to build and deploy your project. 39 | 40 | To check the result, run `docker-compose config`. 41 | To deploy to a swarm write the result to a file `docker-compose config > stack.yml` and use it `docker stack deploy --compose-file stack.yml myproject` 42 | 43 | ### COMPOSE_PROJECT_NAME 44 | A unique project name. It must not contain dots. 45 | E.g. project-typo3version like company-typo3v11 46 | See for more details 47 | 48 | ### COMPOSE_FILE 49 | Add all necessary compose files separated with `:`, always start with the root `docker-compose.yml` to have a proper project name and relative paths. 50 | The settings of the last config wins. 51 | More at 52 | 53 | ### VHOST 54 | The FQDN of the TYPO3 project. 55 | It gets prefixed for other services, e.g. if you set VHOST to `typo3.localhost`, 56 | you can reach Adminer at `adminer-typo3.localhost` and Mailhog at `mailhog-typo3.localhost`. 57 | 58 | ### ADDITIONAL_VHOSTS 59 | Adds additional names to the TYPO3 web service. The value must start with a comma (`,`). 60 | Example `,2nd.domain.tld,3rd.domain.tld` 61 | 62 | ### FRONTEND_NETWORK 63 | The name of the docker network that Traefik can use to connect to the web service. 64 | 65 | ### RESTART 66 | Define the restart policy for all services. 67 | Should be `always` for production and `no` for development. 68 | 69 | ### DB_IMAGE 70 | The image of the db service, see 71 | 72 | * 73 | * 74 | * 75 | 76 | Example `mariadb:10.2` 77 | 78 | ### MYSQL_ROOT_PASSWORD 79 | Set the password of the root db user. 80 | You should not set the password in the `.env` file for production setup. 81 | Set it on CLI 82 | ```bash 83 | MYSQL_ROOT_PASSWORD=MyV3rySecretP4sswd docker-compose up -d 84 | ``` 85 | or set it in CI variables. 86 | 87 | ### DB_BIND_TO 88 | Bind the db service to a specified ip and port. 89 | Format `IP:Port` 90 | Use `127.0.0.1:` to publish a dynamic port to localhost only. 91 | Use `127.0.0.1:13306` to publish the port `13306`. 92 | Use `13306` to publish `13306` to all available IP. ATTENTION! That allows access from anywhere! 93 | The port is mapped to 3306, the MySQL/MariaDB port, inside the container. 94 | See [Access the database during development via tcp](#access-the-database-during-development-via-tcp) 95 | 96 | ### REDIS and LDAP 97 | Build the TYPO3 image with that PHP extensions. 98 | 99 | ### WEB_PORT 100 | The port the web container expose. Only if you use `.docker/env.direct.yml`. 101 | 102 | ### TRAEFIK_ENTRYPOINT_HTTP and TRAEFIK_ENTRYPOINT_HTTPS 103 | Names of the Traefik entrypoints 104 | 105 | ## Build 106 | To build a productive environment use `docker-compose -f .docker/build.yml` from the root with an prepared `.env` 107 | or by setting REDIS and LDAP in the environment of the builder. 108 | If you build on GitLab CI, you can use `.docker/env.gitlab.yml` to tag your images. 109 | See `.gitlab-ci.example.yml`. 110 | 111 | ## Deploy 112 | See `.gitlab-ci.example.yml` for an example how to deploy to docker hosts with GitLab CI. 113 | Consider to set `COMPOSE_PROJECT_NAME` at the deploy job, to be able to deploy the project multiple times to the same docker-host, e.g. testing, staging and live. 114 | 115 | 116 | ## Access the database during development via tcp 117 | A dynamic port is mapped to the database service port 3306. To get this port run: 118 | ```bash 119 | docker-compose ps db 120 | ``` 121 | You'll get something like: 122 | ``` 123 | Name Command State Ports 124 | ------------------------------------------------------------------------------------- 125 | project_db_1 docker-entrypoint.sh --cha ... Up 127.0.0.1:32770->3306/tcp 126 | ``` 127 | where `32770` is the port on the local docker host to connect to. 128 | 129 | ## Run TYPO3 Console commands 130 | 131 | To run a command inside the TYPO3 PHP Container use `docker-compose`: 132 | E.g. flush the cache 133 | ``` 134 | docker-compose exec -u www-data typo3 typo3cms cache:flush 135 | ``` 136 | 137 | ## Import/export the database 138 | 139 | ### Export database 140 | ```shell 141 | docker-compose exec -T db sh -c 'exec mysqldump --opt --single-transaction -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE"' > dump.sql 142 | ``` 143 | ### Import a sql dump 144 | ```shell 145 | docker-compose exec -T db sh -c 'exec mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE"' < dump.sql 146 | # Setup new/development extensions 147 | docker-compose exec -u www-data typo3 typo3cms extension:setupactive --verbose 148 | ``` 149 | 150 | ### Import directly from remote 151 | ```shell 152 | ssh user@server 'TYPO3_CONTEXT="Production" /path/to/typo3cms database:export' | docker-compose exec -T db sh -c 'exec mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE"' 153 | # Setup new/development extensions 154 | docker-compose exec -u www-data typo3 typo3cms extension:setupactive --verbose 155 | ``` 156 | 157 | ### Why can't you use typo3-console to import/export the database to/from the docker environment 158 | The TYPO3 PHP image does not include mysql or mysqldump binary which are required by typo3-console. 159 | 160 | ## Play with docker 161 | Start a demo stack: 162 | * [TYPO3 12](https://labs.play-with-docker.com/?stack=https://raw.githubusercontent.com/t3easy/docker-typo3/12.x/.docker/pwd/stack.yml) 163 | * [TYPO3 11 LTS](https://labs.play-with-docker.com/?stack=https://raw.githubusercontent.com/t3easy/docker-typo3/11.x/.docker/pwd/stack.yml) 164 | * [TYPO3 10 LTS](https://labs.play-with-docker.com/?stack=https://raw.githubusercontent.com/t3easy/docker-typo3/10.x/.docker/pwd/stack.yml) 165 | * [TYPO3 9 LTS](https://labs.play-with-docker.com/?stack=https://raw.githubusercontent.com/t3easy/docker-typo3/9.x/.docker/pwd/stack.yml) 166 | --------------------------------------------------------------------------------