├── wp-content ├── plugins │ └── index.php └── themes │ └── test │ ├── functions.php │ ├── src │ ├── test │ │ ├── js │ │ │ └── app.js │ │ └── css │ │ │ └── app.css │ └── test.js │ ├── dist │ ├── main.css │ ├── main.js │ └── manifest.json │ ├── footer.php │ ├── index.php │ ├── style.css │ ├── header.php │ └── lib │ └── vite.php ├── data ├── cron.conf ├── uploads.ini ├── wpcli-user.sh ├── multisite.htaccess ├── nginx │ └── wordpress.conf └── docker-entrypoint.sh ├── postcss.config.js ├── .editorconfig ├── .dockerignore ├── .gitignore ├── package.json ├── LICENSE ├── docker-compose.yml ├── README.md └── Dockerfile /wp-content/plugins/index.php: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /wp-content/themes/test/src/test.js: -------------------------------------------------------------------------------- 1 | import './test/js/app'; 2 | import './test/css/app.css'; -------------------------------------------------------------------------------- /wp-content/themes/test/src/test/css/app.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #efefef; 3 | } 4 | -------------------------------------------------------------------------------- /data/cron.conf: -------------------------------------------------------------------------------- 1 | # Cron configuration file, set to run WordPress cron once every minute 2 | * * * * * php /usr/src/wordpress/wp-cron.php -------------------------------------------------------------------------------- /data/uploads.ini: -------------------------------------------------------------------------------- 1 | file_uploads = On 2 | memory_limit = 256M 3 | upload_max_filesize = 60M 4 | post_max_size = 60M 5 | max_execution_time = 600 6 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | plugins: { 4 | 'tailwindcss': {}, 5 | "autoprefixer": {}, 6 | 'postcss-nested': {} 7 | } 8 | } -------------------------------------------------------------------------------- /wp-content/themes/test/index.php: -------------------------------------------------------------------------------- 1 | 5 |

Hello World!

6 | 7 | -------------------------------------------------------------------------------- /wp-content/themes/test/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | Theme Name: Test template 3 | Author: This guy 4 | Author URI: http://www.google.com/ 5 | */ 6 | -------------------------------------------------------------------------------- /data/wpcli-user.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a wrapper so that wp-cli can run as the www-data user so that permissions 3 | # remain correct 4 | /usr/local/bin/wp-cli.phar --allow-root "$@" 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | wp-content/themes/**/src 3 | !wp-content/themes 4 | !wp-content/plugins 5 | !data/docker-entrypoint.sh 6 | !data/.well-known 7 | !data/wpcli-user.sh 8 | !data/cron.conf 9 | !data/multisite.htaccess 10 | !data/uploads.ini 11 | 12 | -------------------------------------------------------------------------------- /wp-content/themes/test/dist/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "wp-content/themes/test/src/test.js": { 3 | "file": "main.js", 4 | "src": "wp-content/themes/test/src/test.js", 5 | "isEntry": true, 6 | "css": [ 7 | "main.css" 8 | ] 9 | } 10 | } -------------------------------------------------------------------------------- /wp-content/themes/test/header.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Test Theme 10 | 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /* 2 | data/mysql/.db 3 | data/nginx/log 4 | data/wordpress 5 | data/certs 6 | !.gitignore 7 | !.gitmodules 8 | !.dockerignore 9 | !.editorconfig 10 | !.gitlab-ci.yml 11 | !logo.png 12 | !changelog.md 13 | !LICENSE 14 | !deploy.sh 15 | !docker-compose.yml 16 | !Dockerfile 17 | !package.json 18 | !postcss.config.js 19 | !README.md 20 | !test.docker-compose.yml 21 | !tsconfig.json 22 | !webpack.config.js 23 | !wp-content 24 | wp-content/* 25 | !tests 26 | !wp-content/plugins 27 | !wp-content/themes 28 | wp-content/plugins/hello.php 29 | wp-content/plugins/akismet 30 | wp-content/themes/twenty* 31 | wp-content/themes/index.php 32 | !data/ 33 | *.DS_Store 34 | !kubernetes 35 | -------------------------------------------------------------------------------- /data/multisite.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | RewriteBase / 3 | RewriteRule ^index\.php$ - [L] 4 | 5 | # Allow access from all domains for webfonts. 6 | 7 | Header set Access-Control-Allow-Origin "*" 8 | 9 | 10 | # add a trailing slash to /wp-admin 11 | RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L] 12 | 13 | RewriteCond %{REQUEST_FILENAME} -f [OR] 14 | RewriteCond %{REQUEST_FILENAME} -d 15 | RewriteRule ^ - [L] 16 | RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L] 17 | RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L] 18 | RewriteRule . index.php [L] 19 | -------------------------------------------------------------------------------- /data/nginx/wordpress.conf: -------------------------------------------------------------------------------- 1 | upstream localhost { 2 | server wp:80; 3 | } 4 | 5 | server { 6 | listen 80; 7 | server_name localhost; 8 | index index.php index.html; 9 | error_page 404 index.php?error=404; 10 | client_max_body_size 0; 11 | root /var/www/html; 12 | 13 | location / { 14 | rewrite ^(.*) https://$host$1 permanent; 15 | } 16 | } 17 | 18 | server { 19 | listen 443 ssl; 20 | server_name localhost; 21 | root /var/www/html; 22 | index index.php; 23 | client_max_body_size 0; 24 | 25 | location / { 26 | proxy_pass http://localhost; 27 | proxy_set_header X-Forwarded-Proto https; 28 | } 29 | 30 | 31 | #ssl on; 32 | 33 | ssl_certificate /etc/nginx/certs/ssl.crt; 34 | ssl_certificate_key /etc/nginx/certs/ssl.key; 35 | ssl_dhparam /etc/nginx/certs/dh.pem; 36 | } 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docker-wordpress-multisite", 3 | "version": "0.0.3", 4 | "author": "J Lopes ", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "npm run serve && npm run dev", 8 | "serve": "docker-compose up -d", 9 | "stop": "docker-compose down --remove-orphans", 10 | "dev": "NODE_ENV=development vite build --watch", 11 | "build": "vite build", 12 | "multisite:enable": "docker-compose exec wp sh -c 'wp core multisite-convert && mv .htaccess backup.htaccess'", 13 | "reset": "rm -rf ./data/mysql/.db" 14 | }, 15 | "license": "MIT", 16 | "devDependencies": { 17 | "autoprefixer": "^10.4.4", 18 | "postcss": "^8.5.3", 19 | "postcss-nested": "^5.0.6", 20 | "tailwindcss": "^3.0.23", 21 | "vite": "^7.1.5", 22 | "vite-plugin-live-reload": "^2.1.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 J Lopes 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 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | wp: 3 | build: 4 | context: ./ 5 | dockerfile: Dockerfile 6 | ports: 7 | - "8083:80" 8 | volumes: 9 | - ./wp-content/plugins:/var/www/html/wp-content/plugins:rw 10 | - ./wp-content/themes:/var/www/html/wp-content/themes:rw 11 | - ./data/wordpress:/var/www/html:rw 12 | environment: 13 | - WORDPRESS_DB_HOST=db 14 | - WORDPRESS_DB_NAME=test 15 | - WORDPRESS_DB_USER=root 16 | - WORDPRESS_DB_PASSWORD=test 17 | - WORDPRESS_LOCAL_DEV=true 18 | restart: always 19 | networks: 20 | - test-network 21 | 22 | db: 23 | image: mariadb 24 | restart: always 25 | volumes: 26 | - ./data/mysql/.db/mysql:/var/lib/mysql:rw 27 | - ./data/mysql/:/data/mysql:rw 28 | environment: 29 | - MYSQL_DATABASE=test 30 | - MYSQL_ROOT_PASSWORD=test 31 | - MYSQL_USER=root 32 | ports: 33 | - "13303:3306" 34 | networks: 35 | - test-network 36 | 37 | # nginx: 38 | # image: nginx:alpine 39 | # restart: always 40 | # ports: 41 | # - "8081:80" 42 | # - "443:443" 43 | # depends_on: 44 | # - wp 45 | # volumes: 46 | # - ./data/nginx/wordpress.conf:/etc/nginx/conf.d/default.conf 47 | # - ./data/certs:/etc/nginx/certs/ 48 | # - ./data/nginx/log:/var/log/nginx/ 49 | # - ./data/wordpress:/var/www/html 50 | # - ./wp-content:/var/www/html/wp-content:rw 51 | # networks: 52 | # - test-network 53 | networks: 54 | test-network: 55 | driver: bridge 56 | -------------------------------------------------------------------------------- /wp-content/themes/test/lib/vite.php: -------------------------------------------------------------------------------- 1 | :warning: PHP 7.3 + Webpack version moved to [webpack branch](/pie-inc/docker-wordpress-multisite/tree/webpack) -- The 7.4 version theme makes use of Vite, so there are breaking updates to current developments! 2 | 3 | > :warning: master branch changed to main 4 | 5 | # Docker WordPress Multisite 6 | - [Docker WordPress Multisite](#docker-wordpress-multisite) 7 | - [Pre-requisites](#pre-requisites) 8 | - [Getting started](#getting-started) 9 | - [Enabling multisite](#enabling-multisite) 10 | - [using SSL locally](#using-ssl-locally) 11 | - [FAQ](#faq) 12 | - [Does it work immediately?](#does-it-work-immediately) 13 | - [This is not working properly in Windows... Help!](#this-is-not-working-properly-in-windows-help) 14 | 15 | ## Pre-requisites 16 | ![Nodejs](https://png.icons8.com/color/50/000000/nodejs.png) 17 | * Install [Node](https://nodejs.org/) for your platform. 18 | 19 | ![Docker](https://png.icons8.com/color/50/000000/docker.png) 20 | * Install [Docker](https://www.docker.com/get-docker) for your platform. 21 | * Install [docker-compose](https://docs.docker.com/compose/install/) for your platform (if necessary). 22 | 23 | ## Getting started 24 | Clone this repository: 25 | ``` 26 | git clone git@github.com:pie-inc/docker-wordpress-multisite.git 27 | ``` 28 | 29 | Then install dependencies: ```npm i``` or ```yarn``` 30 | 31 | Once all packages have been installed, run ```yarn start``` to build the docker images, start the docker containers and watch all source files for changes. 32 | 33 | Alternatively you can just run ```npm run serve```, ```yarn serve``` or ```docker-compose up -d```[❔](https://docs.docker.com/compose/reference/up/) to download/build the docker images and start the server. 34 | 35 | A few folders and files will be created inside ```./data``` for debugging, mainly ```mysql```. These folders are local and will not be included in the git flow. 36 | 37 | Go through the famous 5-minute wordpress instalation by going to ```http://localhost:8080``` 38 | 39 | ### Enabling multisite 40 | In line `220` or `data/docker-entrypoint.sh`, update the email section to the admin email. 41 | 42 | Jump into the wordpress container, enable multisite with WP-CLI and update the .htaccess, making a backup of the original. 43 | 44 | ```SHELL 45 | docker-compose exec wp bash 46 | wp core multisite-convert 47 | mv .htaccess backup.htaccess 48 | mv multisite.htaccess .htaccess 49 | ``` 50 | 51 | ### using SSL locally 52 | You'll have to enable nginx in the `docker-compose.yml` file, by removing the comment characters. 53 | Next, you will need install makecert. 54 | 55 | Using homebrew: 56 | ```SHELL 57 | brew install mkcert 58 | brew install nss # if you use Firefox 59 | mkcert -install 60 | ``` 61 | 62 | Then, you will have to generate the Certificates and dh parameters 63 | ```SHELL 64 | mkcert localhost 127.0.0.1 ::1 65 | openssl dhparam -out dh.pem 2066 66 | ``` 67 | 68 | And finally copy the certificates from the mentioned location in the terminal into ```./data/certs/``` 69 | nginx expects the files to have the following naming structure: `dh.pem ssl.crt ssl.key`. The nomenclature can be updated at `data/nginx/wordpress.conf` 70 | 71 | You can also generate your own, or use existing ones you might have. 72 | ([Using openSSL](https://www.openssl.org/docs/manmaster/man1/openssl-req.html)) 73 | 74 | ## FAQ 75 | ### Does it work immediately? 76 | Nope. 77 | 78 | ### This is not working properly in Windows... Help! 79 | Although it might work in Windows, I have only used this process in *NIX machines. Some commands might have to be altered in `package.json` to adapt for proper Windows usage. 80 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.4-apache 2 | 3 | # persistent dependencies 4 | RUN set -eux; \ 5 | apt-get update; \ 6 | apt-get install -y --no-install-recommends \ 7 | # Ghostscript is required for rendering PDF previews 8 | ghostscript \ 9 | ; \ 10 | rm -rf /var/lib/apt/lists/* 11 | 12 | # install the PHP extensions we need (https://make.wordpress.org/hosting/handbook/handbook/server-environment/#php-extensions) 13 | RUN set -ex; \ 14 | \ 15 | savedAptMark="$(apt-mark showmanual)"; \ 16 | \ 17 | apt-get update; \ 18 | apt-get install -y --no-install-recommends \ 19 | libavif-dev \ 20 | libfreetype6-dev \ 21 | libicu-dev \ 22 | libjpeg-dev \ 23 | libmagickwand-dev \ 24 | libpng-dev \ 25 | libwebp-dev \ 26 | libzip-dev \ 27 | ; \ 28 | \ 29 | docker-php-ext-configure gd \ 30 | --with-avif \ 31 | --with-freetype \ 32 | --with-jpeg \ 33 | --with-webp \ 34 | ; \ 35 | docker-php-ext-install -j "$(nproc)" \ 36 | bcmath \ 37 | exif \ 38 | gd \ 39 | intl \ 40 | mysqli \ 41 | zip \ 42 | ; \ 43 | # https://pecl.php.net/package/imagick 44 | pecl install imagick-3.8.0; \ 45 | docker-php-ext-enable imagick; \ 46 | rm -r /tmp/pear; \ 47 | \ 48 | # some misbehaving extensions end up outputting to stdout 🙈 (https://github.com/docker-library/wordpress/issues/669#issuecomment-993945967) 49 | out="$(php -r 'exit(0);')"; \ 50 | [ -z "$out" ]; \ 51 | err="$(php -r 'exit(0);' 3>&1 1>&2 2>&3)"; \ 52 | [ -z "$err" ]; \ 53 | \ 54 | extDir="$(php -r 'echo ini_get("extension_dir");')"; \ 55 | [ -d "$extDir" ]; \ 56 | # reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies 57 | apt-mark auto '.*' > /dev/null; \ 58 | apt-mark manual $savedAptMark; \ 59 | ldd "$extDir"/*.so \ 60 | | awk '/=>/ { so = $(NF-1); if (index(so, "/usr/local/") == 1) { next }; gsub("^/(usr/)?", "", so); printf "*%s\n", so }' \ 61 | | sort -u \ 62 | | xargs -r dpkg-query --search \ 63 | | cut -d: -f1 \ 64 | | sort -u \ 65 | | xargs -rt apt-mark manual; \ 66 | \ 67 | apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ 68 | rm -rf /var/lib/apt/lists/*; \ 69 | \ 70 | ! { ldd "$extDir"/*.so | grep 'not found'; }; \ 71 | # check for output like "PHP Warning: PHP Startup: Unable to load dynamic library 'foo' (tried: ...) 72 | err="$(php --version 3>&1 1>&2 2>&3)"; \ 73 | [ -z "$err" ] 74 | 75 | RUN curl -o /usr/local/bin/wp-cli.phar https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar 76 | COPY ./data/wpcli-user.sh /usr/local/bin/wp 77 | RUN chmod +x /usr/local/bin/wp 78 | RUN chmod +x /usr/local/bin/wp-cli.phar 79 | 80 | # set recommended PHP.ini settings 81 | # see https://secure.php.net/manual/en/opcache.installation.php 82 | RUN set -eux; \ 83 | docker-php-ext-enable opcache; \ 84 | { \ 85 | echo 'opcache.memory_consumption=128'; \ 86 | echo 'opcache.interned_strings_buffer=8'; \ 87 | echo 'opcache.max_accelerated_files=4000'; \ 88 | echo 'opcache.revalidate_freq=2'; \ 89 | } > /usr/local/etc/php/conf.d/opcache-recommended.ini 90 | # https://wordpress.org/support/article/editing-wp-config-php/#configure-error-logging 91 | RUN { \ 92 | # https://www.php.net/manual/en/errorfunc.constants.php 93 | # https://github.com/docker-library/wordpress/issues/420#issuecomment-517839670 94 | echo 'error_reporting = E_ERROR | E_WARNING | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_RECOVERABLE_ERROR'; \ 95 | echo 'display_errors = Off'; \ 96 | echo 'display_startup_errors = Off'; \ 97 | echo 'log_errors = On'; \ 98 | echo 'error_log = /dev/stderr'; \ 99 | echo 'log_errors_max_len = 1024'; \ 100 | echo 'ignore_repeated_errors = On'; \ 101 | echo 'ignore_repeated_source = Off'; \ 102 | echo 'html_errors = Off'; \ 103 | } > /usr/local/etc/php/conf.d/error-logging.ini 104 | 105 | RUN set -eux; \ 106 | a2enmod rewrite expires; \ 107 | \ 108 | # https://httpd.apache.org/docs/2.4/mod/mod_remoteip.html 109 | a2enmod remoteip; \ 110 | { \ 111 | echo 'RemoteIPHeader X-Forwarded-For'; \ 112 | # these IP ranges are reserved for "private" use and should thus *usually* be safe inside Docker 113 | echo 'RemoteIPInternalProxy 10.0.0.0/8'; \ 114 | echo 'RemoteIPInternalProxy 172.16.0.0/12'; \ 115 | echo 'RemoteIPInternalProxy 192.168.0.0/16'; \ 116 | echo 'RemoteIPInternalProxy 169.254.0.0/16'; \ 117 | echo 'RemoteIPInternalProxy 127.0.0.0/8'; \ 118 | } > /etc/apache2/conf-available/remoteip.conf; \ 119 | a2enconf remoteip; \ 120 | # https://github.com/docker-library/wordpress/issues/383#issuecomment-507886512 121 | # (replace all instances of "%h" with "%a" in LogFormat) 122 | find /etc/apache2 -type f -name '*.conf' -exec sed -ri 's/([[:space:]]*LogFormat[[:space:]]+"[^"]*)%h([^"]*")/\1%a\2/g' '{}' + 123 | 124 | COPY ./wp-content/ /var/www/html/wp-content/ 125 | COPY ./data/uploads.ini /usr/local/etc/php/conf.d/uploads.ini 126 | 127 | VOLUME /var/www/html 128 | 129 | EXPOSE 80 130 | 131 | ENV WORDPRESS_VERSION 6.0.1 132 | 133 | RUN set -ex; \ 134 | curl -o wordpress.tar.gz -fSL "https://wordpress.org/wordpress-${WORDPRESS_VERSION}.tar.gz"; \ 135 | tar -xzf wordpress.tar.gz -C /usr/src/; \ 136 | rm wordpress.tar.gz; \ 137 | chown -R www-data:www-data /usr/src/wordpress 138 | 139 | COPY ./data/docker-entrypoint.sh /usr/local/bin/ 140 | COPY ./data/cron.conf /etc/crontabs/www-data 141 | RUN chmod 600 /etc/crontabs/www-data 142 | 143 | ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] 144 | CMD ["apache2-foreground"] 145 | -------------------------------------------------------------------------------- /data/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | # usage: file_env VAR [DEFAULT] 5 | # ie: file_env 'XYZ_DB_PASSWORD' 'example' 6 | # (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of 7 | # "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) 8 | file_env() { 9 | local var="$1" 10 | local fileVar="${var}_FILE" 11 | local def="${2:-}" 12 | if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then 13 | echo >&2 "error: both $var and $fileVar are set (but are exclusive)" 14 | exit 1 15 | fi 16 | local val="$def" 17 | if [ "${!var:-}" ]; then 18 | val="${!var}" 19 | elif [ "${!fileVar:-}" ]; then 20 | val="$(< "${!fileVar}")" 21 | fi 22 | export "$var"="$val" 23 | unset "$fileVar" 24 | } 25 | 26 | if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ]; then 27 | if ! [ -e index.php -a -e wp-includes/version.php ]; then 28 | echo >&2 "WordPress not found in $PWD - copying now..." 29 | if [ "$(ls -A)" ]; then 30 | echo >&2 "WARNING: $PWD is not empty - press Ctrl+C now if this is an error!" 31 | ( set -x; ls -A; sleep 10 ) 32 | fi 33 | tar cf - --one-file-system -C /usr/src/wordpress . | tar xf - 34 | echo >&2 "Complete! WordPress has been successfully copied to $PWD" 35 | if [ ! -e .htaccess ]; then 36 | # NOTE: The "Indexes" option is disabled in the php:apache base image 37 | cat > .htaccess <<-'EOF' 38 | # BEGIN WordPress 39 | 40 | RewriteEngine On 41 | RewriteBase / 42 | RewriteRule ^index\.php$ - [L] 43 | RewriteCond %{REQUEST_FILENAME} !-f 44 | RewriteCond %{REQUEST_FILENAME} !-d 45 | RewriteRule . /index.php [L] 46 | 47 | # END WordPress 48 | EOF 49 | chown www-data:www-data .htaccess 50 | fi 51 | fi 52 | 53 | # TODO handle WordPress upgrades magically in the same way, but only if wp-includes/version.php's $wp_version is less than /usr/src/wordpress/wp-includes/version.php's $wp_version 54 | 55 | # allow any of these "Authentication Unique Keys and Salts." to be specified via 56 | # environment variables with a "WORDPRESS_" prefix (ie, "WORDPRESS_AUTH_KEY") 57 | uniqueEnvs=( 58 | AUTH_KEY 59 | SECURE_AUTH_KEY 60 | LOGGED_IN_KEY 61 | NONCE_KEY 62 | AUTH_SALT 63 | SECURE_AUTH_SALT 64 | LOGGED_IN_SALT 65 | NONCE_SALT 66 | ) 67 | envs=( 68 | WORDPRESS_DB_HOST 69 | WORDPRESS_DB_USER 70 | WORDPRESS_DB_PASSWORD 71 | WORDPRESS_DB_NAME 72 | "${uniqueEnvs[@]/#/WORDPRESS_}" 73 | WORDPRESS_TABLE_PREFIX 74 | WORDPRESS_DEBUG 75 | ) 76 | haveConfig= 77 | for e in "${envs[@]}"; do 78 | file_env "$e" 79 | if [ -z "$haveConfig" ] && [ -n "${!e}" ]; then 80 | haveConfig=1 81 | fi 82 | done 83 | 84 | # linking backwards-compatibility 85 | if [ -n "${!MYSQL_ENV_MYSQL_*}" ]; then 86 | haveConfig=1 87 | # host defaults to "mysql" below if unspecified 88 | : "${WORDPRESS_DB_USER:=${MYSQL_ENV_MYSQL_USER:-root}}" 89 | if [ "$WORDPRESS_DB_USER" = 'root' ]; then 90 | : "${WORDPRESS_DB_PASSWORD:=${MYSQL_ENV_MYSQL_ROOT_PASSWORD:-}}" 91 | else 92 | : "${WORDPRESS_DB_PASSWORD:=${MYSQL_ENV_MYSQL_PASSWORD:-}}" 93 | fi 94 | : "${WORDPRESS_DB_NAME:=${MYSQL_ENV_MYSQL_DATABASE:-}}" 95 | fi 96 | 97 | # only touch "wp-config.php" if we have environment-supplied configuration values 98 | if [ "$haveConfig" ]; then 99 | : "${WORDPRESS_DB_HOST:=mysql}" 100 | : "${WORDPRESS_DB_USER:=root}" 101 | : "${WORDPRESS_DB_PASSWORD:=}" 102 | : "${WORDPRESS_DB_NAME:=wordpress}" 103 | 104 | # version 4.4.1 decided to switch to windows line endings, that breaks our seds and awks 105 | # https://github.com/docker-library/wordpress/issues/116 106 | # https://github.com/WordPress/WordPress/commit/1acedc542fba2482bab88ec70d4bea4b997a92e4 107 | sed -ri -e 's/\r$//' wp-config* 108 | 109 | if [ ! -e wp-config.php ]; then 110 | awk '/^\/\*.*stop editing.*\*\/$/ && c == 0 { c = 1; system("cat") } { print }' wp-config-sample.php > wp-config.php <<'EOPHP' 111 | // If we're behind a proxy server and using HTTPS, we need to alert Wordpress of that fact 112 | // see also http://codex.wordpress.org/Administration_Over_SSL#Using_a_Reverse_Proxy 113 | if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') { 114 | $_SERVER['HTTPS'] = 'on'; 115 | } 116 | 117 | EOPHP 118 | chown www-data:www-data wp-config.php 119 | fi 120 | 121 | # see http://stackoverflow.com/a/2705678/433558 122 | sed_escape_lhs() { 123 | echo "$@" | sed -e 's/[]\/$*.^|[]/\\&/g' 124 | } 125 | sed_escape_rhs() { 126 | echo "$@" | sed -e 's/[\/&]/\\&/g' 127 | } 128 | php_escape() { 129 | php -r 'var_export(('$2') $argv[1]);' -- "$1" 130 | } 131 | set_config() { 132 | key="$1" 133 | value="$2" 134 | var_type="${3:-string}" 135 | start="(['\"])$(sed_escape_lhs "$key")\2\s*," 136 | end="\);" 137 | if [ "${key:0:1}" = '$' ]; then 138 | start="^(\s*)$(sed_escape_lhs "$key")\s*=" 139 | end=";" 140 | fi 141 | sed -ri -e "s/($start\s*).*($end)$/\1$(sed_escape_rhs "$(php_escape "$value" "$var_type")")\3/" wp-config.php 142 | } 143 | 144 | set_config 'DB_HOST' "$WORDPRESS_DB_HOST" 145 | set_config 'DB_USER' "$WORDPRESS_DB_USER" 146 | set_config 'DB_PASSWORD' "$WORDPRESS_DB_PASSWORD" 147 | set_config 'DB_NAME' "$WORDPRESS_DB_NAME" 148 | 149 | for unique in "${uniqueEnvs[@]}"; do 150 | uniqVar="WORDPRESS_$unique" 151 | if [ -n "${!uniqVar}" ]; then 152 | set_config "$unique" "${!uniqVar}" 153 | else 154 | # if not specified, let's generate a random value 155 | currentVal="$(sed -rn -e "s/define\((([\'\"])$unique\2\s*,\s*)(['\"])(.*)\3\);/\4/p" wp-config.php)" 156 | if [ "$currentVal" = 'put your unique phrase here' ]; then 157 | set_config "$unique" "$(head -c1m /dev/urandom | sha1sum | cut -d' ' -f1)" 158 | fi 159 | fi 160 | done 161 | 162 | if [ "$WORDPRESS_TABLE_PREFIX" ]; then 163 | set_config '$table_prefix' "$WORDPRESS_TABLE_PREFIX" 164 | fi 165 | 166 | if [ "$WORDPRESS_DEBUG" ]; then 167 | set_config 'WP_DEBUG' 1 boolean 168 | fi 169 | 170 | TERM=dumb php -- <<'EOPHP' 171 | connect_error) { 194 | fwrite($stderr, "\n" . 'MySQL Connection Error: (' . $mysql->connect_errno . ') ' . $mysql->connect_error . "\n"); 195 | --$maxTries; 196 | if ($maxTries <= 0) { 197 | exit(1); 198 | } 199 | sleep(3); 200 | } 201 | } while ($mysql->connect_error); 202 | 203 | if (!$mysql->query('CREATE DATABASE IF NOT EXISTS `' . $mysql->real_escape_string($dbName) . '`')) { 204 | fwrite($stderr, "\n" . 'MySQL "CREATE DATABASE" Error: ' . $mysql->error . "\n"); 205 | $mysql->close(); 206 | exit(1); 207 | } 208 | 209 | $mysql->close(); 210 | EOPHP 211 | fi 212 | 213 | # now that we're definitely done writing configuration, let's clear out the relevant envrionment variables (so that stray "phpinfo()" calls don't leak secrets from our code) 214 | for e in "${envs[@]}"; do 215 | unset "$e" 216 | done 217 | fi 218 | 219 | if [ -z "${WORDPRESS_LOCAL_DEV+x}" ] && [ ! -f backup.htaccess ]; then 220 | wp core is-installed || wp core multisite-install --skip-config --title="test" --admin_email="" 221 | mv .htaccess backup.htaccess 222 | mv multisite.htaccess .htaccess 223 | wp search-replace "/blog/%year%/%monthnum%/%day%/%postname%/" "blog/%postname%" wp_options 224 | wp rewrite flush 225 | fi 226 | 227 | if [ -d "/var/www/html/wp-content/uploads/cache" ]; then 228 | chown -R www-data:www-data /var/www/html/wp-content/uploads/cache 229 | fi 230 | if [ -d "/var/www/html/wp-content/cache" ]; then 231 | chown -R www-data:www-data /var/www/html/wp-content/cache 232 | fi 233 | 234 | 235 | if [ -d "/var/www/html/wp-content/uploads/cache" ]; then 236 | chown -R www-data:www-data /var/www/html/wp-content/uploads/cache 237 | fi 238 | if [ -d "/var/www/html/wp-content/cache" ]; then 239 | chown -R www-data:www-data /var/www/html/wp-content/cache 240 | fi 241 | 242 | 243 | sed -i "/stop editing/i \ 244 | \@ini_set('session.cookie_httponly', true);\n \ 245 | \@ini_set('session.cookie_secure', true);\n \ 246 | \@ini_set('session.use_only_cookies', true);\n \ 247 | define('WP_MEMORY_LIMIT', '256M');\n \ 248 | define( 'WP_MAX_MEMORY_LIMIT', '256M' );" wp-config.php 249 | 250 | exec "$@" 251 | --------------------------------------------------------------------------------