├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── README.md ├── backups └── .gitignore ├── certbot ├── .certbot.lock └── ssl-dhparam.pem ├── database ├── conf.d │ └── z-mysql.cnf └── phpmyadmin │ └── sql │ └── create_tables.sql.template.example ├── docker-compose.yml ├── env.example ├── install.sh ├── php-fpm ├── php-fpm.d │ └── z-www.conf └── php │ └── conf.d │ └── security.ini ├── phpmyadmin ├── apache2 │ ├── ports.conf │ └── sites-available │ │ ├── 000-default.conf │ │ └── default-ssl.sample.conf ├── config.user.inc.php ├── php │ └── conf.d │ │ └── security.ini └── ssl-option │ └── options-ssl-apache.conf ├── portainer-docker-compose.yml ├── redis └── redis.conf ├── ssl-conf.sh ├── varnish └── default.vcl ├── webserver ├── nginx.conf ├── ssl-option │ └── options-ssl-nginx.conf └── templates │ └── nginx.conf.template └── wordpress └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [damalis] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [full stack nginx WordPress for everyone with docker compose](https://github.com/damalis/full-stack-nginx-wordpress-for-everyone-with-docker-compose) 2 | 3 | If You want to build a website with WordPress at short time; 4 | 5 | #### Full stack Nginx WordPress: 6 |

wordPress     7 | docker     8 | mariadb     9 | mysql     10 | nginx     11 | php     12 | redis     13 | varnish     14 | Bash     15 | phpmyadmin     16 | certbot     17 | letsencrypt     18 | portainer     19 | backup

20 | 21 | Plus, manage docker containers with Portainer. 22 | 23 | #### Supported CPU architectures: 24 |

arm64/aarch64, x86-64

25 | 26 | #### Supported Linux Package Manage Systems: 27 |

apk, dnf, yum, apt/apt-get, zypper, pacman

28 | 29 | #### Supported Linux Operation Systems: 30 |

alpine linux     31 | fedora     32 | centos     33 | debian     34 | ubuntu     35 | ubuntu     36 | redhat on s390x (IBM Z)     37 | opensuse on s390x (IBM Z)     38 | arch linux

39 | 40 | ##### Note: Fedora 37, 39 and alpine linux x86-64 compatible, could not try sles IBM Z s390x, rhel IBM Z s390x and raspberrypi. 41 | 42 | #### With this project you can quickly run the following: 43 | 44 | - [WordPress (php-fpm)](https://hub.docker.com/_/wordpress) 45 | - [webserver (nginx)](https://hub.docker.com/_/nginx) 46 | - [certbot (letsencrypt)](https://hub.docker.com/r/certbot/certbot) 47 | - [phpMyAdmin](https://hub.docker.com/r/phpmyadmin/phpmyadmin/) 48 | - [databaseMariadb](https://hub.docker.com/_/mariadb) [databaseMysql](https://hub.docker.com/_/mysql) 49 | - [redis](https://hub.docker.com/_/redis) 50 | - [varnish](https://hub.docker.com/_/varnish) 51 | - [backup](https://hub.docker.com/r/offen/docker-volume-backup) 52 | 53 | #### For certbot (letsencrypt) certificate: 54 | 55 | - [Set DNS configuration of your domain name](https://support.google.com/a/answer/48090?hl=en) 56 | 57 | #### IPv4/IPv6 Firewall 58 | Create rules to open ports to the internet, or to a specific IPv4 address or range. 59 | 60 | - http: 80 61 | - https: 443 62 | - portainer: 9001 63 | - phpmyadmin: 9090 64 | 65 | #### Note 66 | 67 | To optimize upload images, look at [the damalis repository](https://github.com/damalis/full-stack-nodejs-image-optimizer-for-everyone-with-damalis-repository) 68 | 69 | #### Required Ram 70 | 71 | require up to 2 GB of RAM for **Docker** and **Docker Compose**. 72 | 73 | #### Contents: 74 | 75 | - [Auto Configuration and Installation](#automatic) 76 | - [Requirements](#requirements) 77 | - [Manual Configuration and Installation](#manual) 78 | - [Portainer Installation](#portainer) 79 | - [Usage](#usage) 80 | - [Website](#website) 81 | - [Webserver](#webserver) 82 | - [Redis Plugin](#redis-plugin) 83 | - [Varnish Plugin](#varnish-plugin) 84 | - [phpMyAdmin](#phpmyadmin) 85 | - [backup](#backup) 86 | 87 | ## Automatic 88 | 89 | ### Exec install shell script for auto installation and configuration 90 | 91 | download with 92 | 93 | ``` 94 | git clone https://github.com/damalis/full-stack-nginx-wordpress-for-everyone-with-docker-compose.git 95 | ``` 96 | 97 | Open a terminal and `cd` to the folder in which `docker-compose.yml` is saved and run: 98 | 99 | ``` 100 | cd full-stack-nginx-wordpress-for-everyone-with-docker-compose 101 | chmod +x install.sh 102 | ./install.sh 103 | ``` 104 | 105 | ## Requirements 106 | 107 | Make sure you have the latest versions of **Docker** and **Docker Compose** installed on your machine and require up to 2 GB of RAM. 108 | 109 | - [How install docker](https://docs.docker.com/engine/install/) 110 | - [How install docker compose](https://docs.docker.com/compose/install/) 111 | 112 | Clone this repository or copy the files from this repository into a new folder. 113 | 114 | Make sure to [add your user to the `docker` group](https://docs.docker.com/install/linux/linux-postinstall/#manage-docker-as-a-non-root-user). 115 | 116 | ## Manual 117 | 118 | ### Configuration 119 | 120 | download with 121 | 122 | ``` 123 | git clone https://github.com/damalis/full-stack-nginx-wordpress-for-everyone-with-docker-compose.git 124 | ``` 125 | 126 | Open a terminal and `cd` to the folder in which `docker-compose.yml` is saved and run: 127 | 128 | ``` 129 | cd full-stack-nginx-wordpress-for-everyone-with-docker-compose 130 | ``` 131 | 132 | Copy the example environment into `.env` 133 | 134 | ``` 135 | cp env.example .env 136 | ``` 137 | 138 | Edit the `.env` file to change values of ```LOCAL_TIMEZONE```, ```DOMAIN_NAME```, ```DIRECTORY_PATH```, ```LETSENCRYPT_EMAIL```, ```WORDPRESS_DB_USER```, ```WORDPRESS_DB_PASSWORD```, ```WORDPRESS_DB_NAME```, ```WORDPRESS_TABLE_PREFIX```, ```MYSQL_ROOT_PASSWORD```, ```DATABASE_IMAGE_NAME```, ```DATABASE_CONT_NAME```, ```DATABASE_PACKAGE_MANAGER```, ```DATABASE_ADMIN_COMMANDLINE```, ```PMA_CONTROLUSER```, ```PMA_CONTROLPASS```, ```PMA_HTPASSWD_USERNAME```, ```PMA_HTPASSWD_PASSWORD``` and ```VARNISH_VERSION```. 139 | 140 | LOCAL_TIMEZONE=[to see local timezones](https://docs.diladele.com/docker/timezones.html) 141 | 142 | DIRECTORY_PATH=```pwd``` at command line\ 143 | DATABASE_IMAGE_NAME=```mariadb``` or ```mysql```\ 144 | DATABASE_CONT_NAME=```mariadb```, ```mysql``` or ```custom name```\ 145 | DATABASE_PACKAGE_MANAGER=```apt-get update && apt-get install -y gettext-base``` for mariadb, ```microdnf install -y gettext``` for mysql\ 146 | DATABASE_ADMIN_COMMANDLINE=```mariadb-admin``` for mariadb, ```mysqladmin``` for mysql\ 147 | VARNISH_VERSION=```latest``` for centos version 9+ and fedora, ```stable``` for the others\ 148 | SSL_SNIPPET=```echo 'Generated Self-signed SSL Certificate for localhost'``` for localhost\ 149 | SSL_SNIPPET=```certbot certonly --webroot --webroot-path /tmp/acme-challenge --rsa-key-size 4096 --non-interactive --agree-tos --no-eff-email --force-renewal --email ${LETSENCRYPT_EMAIL} -d ${DOMAIN_NAME} -d www.${DOMAIN_NAME}``` for remotehost 150 | 151 | and 152 | 153 | ``` 154 | cp ./phpmyadmin/apache2/sites-available/default-ssl.sample.conf ./phpmyadmin/apache2/sites-available/default-ssl.conf 155 | ``` 156 | change example.com to your domain name in ```./phpmyadmin/apache2/sites-available/default-ssl.conf``` file. 157 | 158 | ``` 159 | cp ./database/phpmyadmin/sql/create_tables.sql.template.example ./database/phpmyadmin/sql/create_tables.sql.template 160 | ``` 161 | change pma_controluser and db_authentication_password in ```./database/phpmyadmin/sql/create_tables.sql.template``` file. 162 | 163 | ### Installation 164 | 165 | Firstly: will create external volume 166 | 167 | ``` 168 | docker volume create --driver local --opt type=none --opt device=${PWD}/certbot --opt o=bind certbot-etc 169 | ``` 170 | 171 | for localhost ssl: Generate Self-signed SSL Certificate with guide [mkcert repository](https://github.com/FiloSottile/mkcert). 172 | 173 | ``` 174 | docker compose up -d 175 | ``` 176 | 177 | then reloading for webserver ssl configuration 178 | 179 | ``` 180 | docker container restart webserver 181 | ``` 182 | 183 | The containers are now built and running. You should be able to access the WordPress installation with the configured IP in the browser address. `https://example.com`. 184 | 185 | For convenience you may add a new entry into your hosts file. 186 | 187 | ## Portainer 188 | 189 | ``` 190 | docker compose -f portainer-docker-compose.yml -p portainer up -d 191 | ``` 192 | 193 | manage docker with [Portainer](https://www.portainer.io/) is the definitive container management tool for Docker, Docker Swarm with it's highly intuitive GUI and API. 194 | 195 | You can also visit `https://example.com:9001` to access portainer after starting the containers. 196 | 197 | ## Usage 198 | 199 | #### You could manage docker containers without command line with portainer. 200 | 201 | ### Show both running and stopped containers 202 | 203 | The docker ps command only shows running containers by default. To see all containers, use the -a (or --all) flag: 204 | 205 | ``` 206 | docker ps -a 207 | ``` 208 | 209 | ### Starting containers 210 | 211 | You can start the containers with the `up` command in daemon mode (by adding `-d` as an argument) or by using the `start` command: 212 | 213 | ``` 214 | docker compose start 215 | ``` 216 | 217 | ### Stopping containers 218 | 219 | ``` 220 | docker compose stop 221 | ``` 222 | 223 | ### Removing containers 224 | 225 | To stop and remove all the containers use the `down` command: 226 | 227 | ``` 228 | docker compose down 229 | ``` 230 | 231 | to remove portainer and the other containers: 232 | 233 | ``` 234 | docker rm -f $(docker ps -a -q) 235 | ``` 236 | 237 | Use `-v` if you need to remove the database volume which is used to persist the database: 238 | 239 | ``` 240 | docker compose down -v 241 | ``` 242 | 243 | to remove external certbot-etc and portainer and the other volumes: 244 | 245 | ``` 246 | docker volume rm $(docker volume ls -q) 247 | ``` 248 | 249 | Delete all images, containers, volumes, and networks that are not associated with a container (dangling): 250 | 251 | ``` 252 | docker system prune 253 | ``` 254 | 255 | To additionally remove any stopped containers and all unused images (not just dangling ones), add the -a flag to the command: 256 | 257 | ``` 258 | docker system prune -a 259 | ``` 260 | 261 | to remove portainer and the other images: 262 | 263 | ``` 264 | docker rmi $(docker image ls -q) 265 | ``` 266 | 267 | ### Logs containers 268 | 269 | To fetch the logs of a container. 270 | 271 | ``` 272 | docker container logs container_name_or_id 273 | ``` 274 | 275 | ### Project from existing source 276 | 277 | Copy all files into a new directory: 278 | 279 | You can now use the `up` command: 280 | 281 | ``` 282 | docker compose up -d 283 | ``` 284 | 285 | ### Docker run reference 286 | 287 | [https://docs.docker.com/engine/reference/run/](https://docs.docker.com/engine/reference/run/) 288 | 289 | ### Website 290 | 291 | You should see the "Wordpress installation" page in your browser. If not, please check if your PHP installation satisfies WordPress's requirements. 292 | 293 | ``` 294 | https://example.com 295 | ``` 296 | 297 | add or remove code in the ./php-fpm/php/conf.d/security.ini file for custom php.ini configurations 298 | 299 | [https://www.php.net/manual/en/configuration.file.php](https://www.php.net/manual/en/configuration.file.php) 300 | 301 | You should make changes custom host configurations ```./php-fpm/php-fpm.d/z-www.conf``` then must restart service, FPM uses php.ini syntax for its configuration file - php-fpm.conf, and pool configuration files. 302 | 303 | [https://www.php.net/manual/en/install.fpm.configuration.php](https://www.php.net/manual/en/install.fpm.configuration.php) 304 | 305 | ``` 306 | docker container restart wordpress 307 | ``` 308 | 309 | add and/or remove wordpress site folders and files with any ftp client program in ```./wordpress``` folder. 310 |
You can also visit `https://example.com` to access website after starting the containers. 311 | 312 | #### Webserver 313 | 314 | add or remove code in the ```./webserver/templates/nginx.conf.template``` file for custom nginx configurations 315 | 316 | [https://docs.nginx.com/nginx/admin-guide/basic-functionality/managing-configuration-files/](https://docs.nginx.com/nginx/admin-guide/basic-functionality/managing-configuration-files/) 317 | 318 | #### Redis Plugin 319 | 320 | add and active [Redis Cache](https://wordpress.org/plugins/redis-cache/) plugin and 321 | 322 | must add below code in wp-config.php file. 323 | 324 | ``` 325 | define('WP_REDIS_HOST', 'redis'); 326 | define('WP_CACHE_KEY_SALT', 'wp-docker-7f1a7682-9aec-4d4b-9a10-46bbadec41ba'); 327 | define('WP_REDIS_PREFIX', $_SERVER['HTTP_HOST']); 328 | define('WP_REDIS_CONFIG', [ 329 | 'prefix' => getenv('WP_REDIS_PREFIX') ?: null, 330 | 'timeout' => 0.5, 331 | 'read_timeout' => 0.5, 332 | 'async_flush' => true, 333 | 'compression' => 'zstd', 334 | 'serializer' => 'igbinary', 335 | 'split_alloptions' => true, 336 | 'debug' => false, 337 | 'save_commands' => false, 338 | ]); 339 | ``` 340 | 341 | #### Varnish Plugin 342 | 343 | add and active [Proxy Cache Purge](https://wordpress.org/plugins/varnish-http-purge/) plugin. 344 | 345 | ##### 346 | Go to the WordPress dashboard
347 | Click on Plugins
348 | Click on Add New
349 | Search for the Redis Cache / the Proxy Cache Purge plugin
350 | Click on Install Now and confirm
351 | Finally, activate the plugin 352 | 353 | add this code to connect always with ssl in wp-config.php file. 354 | 355 | ``` 356 | define('FORCE_SSL_LOGIN', true); 357 | define('FORCE_SSL_ADMIN', true); 358 | ``` 359 | 360 | after every change in the wordpress and the varnish configuration or if You get error "502 Bad Gateway": 361 | 362 | ``` 363 | docker container restart varnish 364 | ``` 365 | 366 | ### phpMyAdmin 367 | 368 | You can add your own custom config.inc.php settings (such as Configuration Storage setup) by creating a file named config.user.inc.php with the various user defined settings in it, and then linking it into the container using: 369 | 370 | ``` 371 | ./phpmyadmin/config.user.inc.php 372 | ``` 373 | 374 | You can also visit `https://example.com:9090` to access phpMyAdmin after starting the containers. 375 | 376 | The first authorize screen(htpasswd;username or password) and phpmyadmin login screen the username and the password is the same as supplied in the `.env` file. 377 | 378 | ### backup 379 | 380 | This will back up the all files and folders in database/dump sql and html volumes, once per day, and write it to ./backups with a filename like backup-2023-01-01T10-18-00.tar.gz 381 | 382 | #### can run on a custom cron schedule 383 | 384 | ```BACKUP_CRON_EXPRESSION: '20 01 * * *'``` the UTC timezone. 385 | -------------------------------------------------------------------------------- /backups/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore -------------------------------------------------------------------------------- /certbot/.certbot.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damalis/full-stack-nginx-wordpress-for-everyone-with-docker-compose/a3dd37f47be66b77930a7e4416af186279070796/certbot/.certbot.lock -------------------------------------------------------------------------------- /certbot/ssl-dhparam.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN DH PARAMETERS----- 2 | MIIBCAKCAQEA3T1FEu1mdoEgqUL/v7OPEGd8yp+/2nUHyRFeyx9UQu7GXWMk7SSC 3 | ob/WE62HKtTiL3GskWJYh5HCPcBOETCWtbeib4xX4x/M7fzRU+io7hraIXPq3f1/ 4 | /9KMljeQF8YqX913DU0WbeA8gJrpWEKSTiYkKBoS5K0AfgQDcDv+rHD8vOANfU/h 5 | YtR/Pjidl8TL/64fyWnGLn1l6VMzyYqgxGIlVX51fdwkO1KPpI+nLvURv7iXph3H 6 | WdpZl7wT1kcctjqH84MFBb4CotzUceY/+L3JOtUMkQbf68nB6Fwrx63+9IEYN9of 7 | 0pyDWBhM9NbnJUHZsJEBq49T4FPlMJCiMwIBAg== 8 | -----END DH PARAMETERS----- 9 | -------------------------------------------------------------------------------- /database/conf.d/z-mysql.cnf: -------------------------------------------------------------------------------- 1 | [client] 2 | 3 | [mysqld] 4 | 5 | [mysqldump] 6 | 7 | [mysql] 8 | 9 | [mysqlhotcopy] 10 | 11 | [mysqld_safe] 12 | -------------------------------------------------------------------------------- /database/phpmyadmin/sql/create_tables.sql.template.example: -------------------------------------------------------------------------------- 1 | -- -------------------------------------------------------- 2 | -- SQL Commands to set up the pmadb as described in the documentation. 3 | -- 4 | -- This file is meant for use with MySQL 5 and above! 5 | -- 6 | -- This script expects the user pma to already be existing. If we would put a 7 | -- line here to create them too many users might just use this script and end 8 | -- up with having the same password for the controluser. 9 | -- 10 | -- This user "pma" must be defined in config.inc.php (controluser/controlpass) 11 | -- 12 | -- Please don't forget to set up the tablenames in config.inc.php 13 | -- 14 | 15 | -- -------------------------------------------------------- 16 | 17 | -- 18 | -- Database : `phpmyadmin` 19 | -- 20 | CREATE DATABASE IF NOT EXISTS `phpmyadmin` 21 | DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 22 | USE phpmyadmin; 23 | 24 | -- -------------------------------------------------------- 25 | 26 | -- 27 | -- CREATE USER IF NOT EXISTS for phpmyadmin database 28 | -- 29 | CREATE USER IF NOT EXISTS 'pma_controluser'@'%' IDENTIFIED BY 'db_authentication_password'; 30 | 31 | -- 32 | -- Privileges 33 | -- 34 | -- (activate this statement if necessary) 35 | GRANT SELECT, INSERT, DELETE, UPDATE, ALTER ON `phpmyadmin`.* TO 'pma_controluser'@'%'; 36 | 37 | -- -------------------------------------------------------- 38 | 39 | -- 40 | -- Table structure for table `pma__bookmark` 41 | -- 42 | 43 | CREATE TABLE IF NOT EXISTS `pma__bookmark` ( 44 | `id` int(10) unsigned NOT NULL auto_increment, 45 | `dbase` varchar(255) NOT NULL default '', 46 | `user` varchar(255) NOT NULL default '', 47 | `label` varchar(255) COLLATE utf8_general_ci NOT NULL default '', 48 | `query` text NOT NULL, 49 | PRIMARY KEY (`id`) 50 | ) 51 | COMMENT='Bookmarks' 52 | DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 53 | 54 | -- -------------------------------------------------------- 55 | 56 | -- 57 | -- Table structure for table `pma__column_info` 58 | -- 59 | 60 | CREATE TABLE IF NOT EXISTS `pma__column_info` ( 61 | `id` int(5) unsigned NOT NULL auto_increment, 62 | `db_name` varchar(64) NOT NULL default '', 63 | `table_name` varchar(64) NOT NULL default '', 64 | `column_name` varchar(64) NOT NULL default '', 65 | `comment` varchar(255) COLLATE utf8_general_ci NOT NULL default '', 66 | `mimetype` varchar(255) COLLATE utf8_general_ci NOT NULL default '', 67 | `transformation` varchar(255) NOT NULL default '', 68 | `transformation_options` varchar(255) NOT NULL default '', 69 | `input_transformation` varchar(255) NOT NULL default '', 70 | `input_transformation_options` varchar(255) NOT NULL default '', 71 | PRIMARY KEY (`id`), 72 | UNIQUE KEY `db_name` (`db_name`,`table_name`,`column_name`) 73 | ) 74 | COMMENT='Column information for phpMyAdmin' 75 | DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 76 | 77 | -- -------------------------------------------------------- 78 | 79 | -- 80 | -- Table structure for table `pma__history` 81 | -- 82 | 83 | CREATE TABLE IF NOT EXISTS `pma__history` ( 84 | `id` bigint(20) unsigned NOT NULL auto_increment, 85 | `username` varchar(64) NOT NULL default '', 86 | `db` varchar(64) NOT NULL default '', 87 | `table` varchar(64) NOT NULL default '', 88 | `timevalue` timestamp NOT NULL default CURRENT_TIMESTAMP, 89 | `sqlquery` text NOT NULL, 90 | PRIMARY KEY (`id`), 91 | KEY `username` (`username`,`db`,`table`,`timevalue`) 92 | ) 93 | COMMENT='SQL history for phpMyAdmin' 94 | DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 95 | 96 | -- -------------------------------------------------------- 97 | 98 | -- 99 | -- Table structure for table `pma__pdf_pages` 100 | -- 101 | 102 | CREATE TABLE IF NOT EXISTS `pma__pdf_pages` ( 103 | `db_name` varchar(64) NOT NULL default '', 104 | `page_nr` int(10) unsigned NOT NULL auto_increment, 105 | `page_descr` varchar(50) COLLATE utf8_general_ci NOT NULL default '', 106 | PRIMARY KEY (`page_nr`), 107 | KEY `db_name` (`db_name`) 108 | ) 109 | COMMENT='PDF relation pages for phpMyAdmin' 110 | DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 111 | 112 | -- -------------------------------------------------------- 113 | 114 | -- 115 | -- Table structure for table `pma__recent` 116 | -- 117 | 118 | CREATE TABLE IF NOT EXISTS `pma__recent` ( 119 | `username` varchar(64) NOT NULL, 120 | `tables` text NOT NULL, 121 | PRIMARY KEY (`username`) 122 | ) 123 | COMMENT='Recently accessed tables' 124 | DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 125 | 126 | -- -------------------------------------------------------- 127 | 128 | -- 129 | -- Table structure for table `pma__favorite` 130 | -- 131 | 132 | CREATE TABLE IF NOT EXISTS `pma__favorite` ( 133 | `username` varchar(64) NOT NULL, 134 | `tables` text NOT NULL, 135 | PRIMARY KEY (`username`) 136 | ) 137 | COMMENT='Favorite tables' 138 | DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 139 | 140 | -- -------------------------------------------------------- 141 | 142 | -- 143 | -- Table structure for table `pma__table_uiprefs` 144 | -- 145 | 146 | CREATE TABLE IF NOT EXISTS `pma__table_uiprefs` ( 147 | `username` varchar(64) NOT NULL, 148 | `db_name` varchar(64) NOT NULL, 149 | `table_name` varchar(64) NOT NULL, 150 | `prefs` text NOT NULL, 151 | `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 152 | PRIMARY KEY (`username`,`db_name`,`table_name`) 153 | ) 154 | COMMENT='Tables'' UI preferences' 155 | DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 156 | 157 | -- -------------------------------------------------------- 158 | 159 | -- 160 | -- Table structure for table `pma__relation` 161 | -- 162 | 163 | CREATE TABLE IF NOT EXISTS `pma__relation` ( 164 | `master_db` varchar(64) NOT NULL default '', 165 | `master_table` varchar(64) NOT NULL default '', 166 | `master_field` varchar(64) NOT NULL default '', 167 | `foreign_db` varchar(64) NOT NULL default '', 168 | `foreign_table` varchar(64) NOT NULL default '', 169 | `foreign_field` varchar(64) NOT NULL default '', 170 | PRIMARY KEY (`master_db`,`master_table`,`master_field`), 171 | KEY `foreign_field` (`foreign_db`,`foreign_table`) 172 | ) 173 | COMMENT='Relation table' 174 | DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 175 | 176 | -- -------------------------------------------------------- 177 | 178 | -- 179 | -- Table structure for table `pma__table_coords` 180 | -- 181 | 182 | CREATE TABLE IF NOT EXISTS `pma__table_coords` ( 183 | `db_name` varchar(64) NOT NULL default '', 184 | `table_name` varchar(64) NOT NULL default '', 185 | `pdf_page_number` int(11) NOT NULL default '0', 186 | `x` float unsigned NOT NULL default '0', 187 | `y` float unsigned NOT NULL default '0', 188 | PRIMARY KEY (`db_name`,`table_name`,`pdf_page_number`) 189 | ) 190 | COMMENT='Table coordinates for phpMyAdmin PDF output' 191 | DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 192 | 193 | -- -------------------------------------------------------- 194 | 195 | -- 196 | -- Table structure for table `pma__table_info` 197 | -- 198 | 199 | CREATE TABLE IF NOT EXISTS `pma__table_info` ( 200 | `db_name` varchar(64) NOT NULL default '', 201 | `table_name` varchar(64) NOT NULL default '', 202 | `display_field` varchar(64) NOT NULL default '', 203 | PRIMARY KEY (`db_name`,`table_name`) 204 | ) 205 | COMMENT='Table information for phpMyAdmin' 206 | DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 207 | 208 | -- -------------------------------------------------------- 209 | 210 | -- 211 | -- Table structure for table `pma__tracking` 212 | -- 213 | 214 | CREATE TABLE IF NOT EXISTS `pma__tracking` ( 215 | `db_name` varchar(64) NOT NULL, 216 | `table_name` varchar(64) NOT NULL, 217 | `version` int(10) unsigned NOT NULL, 218 | `date_created` datetime NOT NULL, 219 | `date_updated` datetime NOT NULL, 220 | `schema_snapshot` text NOT NULL, 221 | `schema_sql` text, 222 | `data_sql` longtext, 223 | `tracking` set('UPDATE','REPLACE','INSERT','DELETE','TRUNCATE','CREATE DATABASE','ALTER DATABASE','DROP DATABASE','CREATE TABLE','ALTER TABLE','RENAME TABLE','DROP TABLE','CREATE INDEX','DROP INDEX','CREATE VIEW','ALTER VIEW','DROP VIEW') default NULL, 224 | `tracking_active` int(1) unsigned NOT NULL default '1', 225 | PRIMARY KEY (`db_name`,`table_name`,`version`) 226 | ) 227 | COMMENT='Database changes tracking for phpMyAdmin' 228 | DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 229 | 230 | -- -------------------------------------------------------- 231 | 232 | -- 233 | -- Table structure for table `pma__userconfig` 234 | -- 235 | 236 | CREATE TABLE IF NOT EXISTS `pma__userconfig` ( 237 | `username` varchar(64) NOT NULL, 238 | `timevalue` timestamp NOT NULL default CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 239 | `config_data` text NOT NULL, 240 | PRIMARY KEY (`username`) 241 | ) 242 | COMMENT='User preferences storage for phpMyAdmin' 243 | DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 244 | 245 | -- -------------------------------------------------------- 246 | 247 | -- 248 | -- Table structure for table `pma__users` 249 | -- 250 | 251 | CREATE TABLE IF NOT EXISTS `pma__users` ( 252 | `username` varchar(64) NOT NULL, 253 | `usergroup` varchar(64) NOT NULL, 254 | PRIMARY KEY (`username`,`usergroup`) 255 | ) 256 | COMMENT='Users and their assignments to user groups' 257 | DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 258 | 259 | -- -------------------------------------------------------- 260 | 261 | -- 262 | -- Table structure for table `pma__usergroups` 263 | -- 264 | 265 | CREATE TABLE IF NOT EXISTS `pma__usergroups` ( 266 | `usergroup` varchar(64) NOT NULL, 267 | `tab` varchar(64) NOT NULL, 268 | `allowed` enum('Y','N') NOT NULL DEFAULT 'N', 269 | PRIMARY KEY (`usergroup`,`tab`,`allowed`) 270 | ) 271 | COMMENT='User groups with configured menu items' 272 | DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 273 | 274 | -- -------------------------------------------------------- 275 | 276 | -- 277 | -- Table structure for table `pma__navigationhiding` 278 | -- 279 | 280 | CREATE TABLE IF NOT EXISTS `pma__navigationhiding` ( 281 | `username` varchar(64) NOT NULL, 282 | `item_name` varchar(64) NOT NULL, 283 | `item_type` varchar(64) NOT NULL, 284 | `db_name` varchar(64) NOT NULL, 285 | `table_name` varchar(64) NOT NULL, 286 | PRIMARY KEY (`username`,`item_name`,`item_type`,`db_name`,`table_name`) 287 | ) 288 | COMMENT='Hidden items of navigation tree' 289 | DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 290 | 291 | -- -------------------------------------------------------- 292 | 293 | -- 294 | -- Table structure for table `pma__savedsearches` 295 | -- 296 | 297 | CREATE TABLE IF NOT EXISTS `pma__savedsearches` ( 298 | `id` int(5) unsigned NOT NULL auto_increment, 299 | `username` varchar(64) NOT NULL default '', 300 | `db_name` varchar(64) NOT NULL default '', 301 | `search_name` varchar(64) NOT NULL default '', 302 | `search_data` text NOT NULL, 303 | PRIMARY KEY (`id`), 304 | UNIQUE KEY `u_savedsearches_username_dbname` (`username`,`db_name`,`search_name`) 305 | ) 306 | COMMENT='Saved searches' 307 | DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 308 | 309 | -- -------------------------------------------------------- 310 | 311 | -- 312 | -- Table structure for table `pma__central_columns` 313 | -- 314 | 315 | CREATE TABLE IF NOT EXISTS `pma__central_columns` ( 316 | `db_name` varchar(64) NOT NULL, 317 | `col_name` varchar(64) NOT NULL, 318 | `col_type` varchar(64) NOT NULL, 319 | `col_length` text, 320 | `col_collation` varchar(64) NOT NULL, 321 | `col_isNull` boolean NOT NULL, 322 | `col_extra` varchar(255) default '', 323 | `col_default` text, 324 | PRIMARY KEY (`db_name`,`col_name`) 325 | ) 326 | COMMENT='Central list of columns' 327 | DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 328 | 329 | -- -------------------------------------------------------- 330 | 331 | -- 332 | -- Table structure for table `pma__designer_settings` 333 | -- 334 | 335 | CREATE TABLE IF NOT EXISTS `pma__designer_settings` ( 336 | `username` varchar(64) NOT NULL, 337 | `settings_data` text NOT NULL, 338 | PRIMARY KEY (`username`) 339 | ) 340 | COMMENT='Settings related to Designer' 341 | DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 342 | 343 | -- -------------------------------------------------------- 344 | 345 | -- 346 | -- Table structure for table `pma__export_templates` 347 | -- 348 | 349 | CREATE TABLE IF NOT EXISTS `pma__export_templates` ( 350 | `id` int(5) unsigned NOT NULL AUTO_INCREMENT, 351 | `username` varchar(64) NOT NULL, 352 | `export_type` varchar(10) NOT NULL, 353 | `template_name` varchar(64) NOT NULL, 354 | `template_data` text NOT NULL, 355 | PRIMARY KEY (`id`), 356 | UNIQUE KEY `u_user_type_template` (`username`,`export_type`,`template_name`) 357 | ) 358 | COMMENT='Saved export templates' 359 | DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 360 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | 3 | wordpress: 4 | depends_on: 5 | database: 6 | condition: service_healthy 7 | image: wordpress:${PHP_IMAGE_NAME} 8 | container_name: wordpress 9 | networks: 10 | - backend 11 | volumes: 12 | - 'html:${WEBSERVER_DOC_ROOT}' 13 | - type: bind 14 | source: ./php-fpm/php/conf.d/security.ini 15 | target: '${PHP_INI_DIR_PREFIX}/php/conf.d/security.ini' 16 | - type: bind 17 | source: ./php-fpm/php-fpm.d/z-www.conf 18 | target: '${PHP_INI_DIR_PREFIX}/php-fpm.d/z-www.conf' 19 | hostname: wordpress 20 | restart: unless-stopped 21 | ports: 22 | - '9000:80' 23 | links: 24 | - database 25 | - redis 26 | healthcheck: 27 | test: ["CMD-SHELL", "/bin/pidof php-fpm > /dev/null || exit 1"] 28 | interval: 5s 29 | timeout: 5s 30 | retries: 20 31 | environment: 32 | WORDPRESS_DB_HOST: 'database' 33 | WORDPRESS_DB_USER: '${WORDPRESS_DB_USER}' 34 | WORDPRESS_DB_PASSWORD: '${WORDPRESS_DB_PASSWORD}' 35 | WORDPRESS_DB_NAME: '${WORDPRESS_DB_NAME}' 36 | WORDPRESS_TABLE_PREFIX: '${WORDPRESS_TABLE_PREFIX}' 37 | HOME: '/tmp' 38 | TZ: '${LOCAL_TIMEZONE}' 39 | labels: 40 | - 'docker-volume-backup.stop-during-backup=true' 41 | command: bash -c "curl -sSL https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions -o - | sh -s redis && grep -qe 'date.timezone = ${LOCAL_TIMEZONE}' ${PHP_INI_DIR_PREFIX}/php/conf.d/security.ini || echo 'date.timezone = ${LOCAL_TIMEZONE}' >> ${PHP_INI_DIR_PREFIX}/php/conf.d/security.ini; docker-entrypoint.sh 'php-fpm'" 42 | 43 | webserver: 44 | depends_on: 45 | - varnish 46 | image: nginx:stable 47 | container_name: webserver 48 | networks: 49 | - backend 50 | - frontend 51 | volumes: 52 | - 'html:${WEBSERVER_DOC_ROOT}' 53 | - type: bind 54 | source: ./webserver/nginx.conf 55 | target: '${NGINX_PREFIX}/nginx.conf' 56 | - type: bind 57 | source: ./webserver/templates/nginx.conf.template 58 | target: '${NGINX_PREFIX}/templates/default.conf.template' 59 | - type: bind 60 | source: ./webserver/ssl-option/options-ssl-nginx.conf 61 | target: '${LETSENCRYPT_CONF_PREFIX}/options-ssl-nginx.conf' 62 | - type: bind 63 | source: ./ssl-conf.sh 64 | target: '/tmp/ssl-conf.sh' 65 | - 'certbot-etc:${LETSENCRYPT_CONF_PREFIX}' 66 | - '/tmp/acme-challenge:/tmp/acme-challenge' 67 | hostname: webserver 68 | restart: unless-stopped 69 | ports: 70 | - '80:80' 71 | - '443:443' 72 | - '90:90' 73 | links: 74 | - wordpress 75 | environment: 76 | NGINX_HOST: ${DOMAIN_NAME} 77 | NGINX_PORT: 80 78 | TZ: '${LOCAL_TIMEZONE}' 79 | command: bash -c "/docker-entrypoint.sh nginx -v; sh /tmp/ssl-conf.sh '${DOMAIN_NAME}' '${LETSENCRYPT_CONF_PREFIX}' '${NGINX_PREFIX}'" 80 | 81 | certbot: 82 | depends_on: 83 | - webserver 84 | image: certbot/certbot:latest 85 | container_name: certbot 86 | networks: 87 | - backend 88 | volumes: 89 | - 'certbot-etc:${LETSENCRYPT_CONF_PREFIX}' 90 | - 'certbot-var:/var/lib/letsencrypt' 91 | - '/tmp/acme-challenge:/tmp/acme-challenge' 92 | restart: unless-stopped 93 | healthcheck: 94 | test: ["CMD-SHELL", "test -d ${LETSENCRYPT_CONF_PREFIX}/live/${DOMAIN_NAME} || exit 1"] 95 | interval: 5s 96 | timeout: 5s 97 | retries: 20 98 | environment: 99 | TZ: '${LOCAL_TIMEZONE}' 100 | entrypoint: /bin/sh -c "${SSL_SNIPPET}; trap exit TERM; while :; do certbot renew --dry-run; sleep 12h & wait $${!}; done;" 101 | 102 | phpmyadmin: 103 | depends_on: 104 | certbot: 105 | condition: service_healthy 106 | image: phpmyadmin:latest 107 | container_name: phpmyadmin 108 | networks: 109 | - backend 110 | - frontend 111 | volumes: 112 | - type: bind 113 | source: ./phpmyadmin/apache2/sites-available/default-ssl.conf 114 | target: '${APACHE_CONFDIR_PREFIX}/sites-available/default-ssl.conf' 115 | - type: bind 116 | source: ./phpmyadmin/apache2/ports.conf 117 | target: '${APACHE_CONFDIR_PREFIX}/ports.conf' 118 | - type: bind 119 | source: ./phpmyadmin/ssl-option/options-ssl-apache.conf 120 | target: '${LETSENCRYPT_CONF_PREFIX}/options-ssl-apache.conf' 121 | - type: bind 122 | source: ./phpmyadmin/config.user.inc.php 123 | target: '${PMA_CONF_FOLDER}/config.user.inc.php' 124 | - type: bind 125 | source: ./phpmyadmin/php/conf.d/security.ini 126 | target: '${PHP_INI_DIR_PREFIX}/php/conf.d/security.ini' 127 | - 'certbot-etc:${LETSENCRYPT_CONF_PREFIX}' 128 | hostname: phpmyadmin 129 | restart: unless-stopped 130 | ports: 131 | - '9090:443' 132 | links: 133 | - database 134 | environment: 135 | PMA_HOST: 'database' 136 | PMA_PMADB: 'phpmyadmin' 137 | PMA_CONTROLUSER: '${PMA_CONTROLUSER}' 138 | PMA_CONTROLPASS: '${PMA_CONTROLPASS}' 139 | MYSQL_ROOT_PASSWORD: '${MYSQL_ROOT_PASSWORD}' 140 | UPLOAD_LIMIT: '${PMA_UPLOAD_LIMIT}' 141 | MEMORY_LIMIT: '${PMA_MEMORY_LIMIT}' 142 | TZ: '${LOCAL_TIMEZONE}' 143 | command: > 144 | bash -c "echo ${PMA_HTPASSWD_USERNAME}:phpmyadmin:$$( printf \"%s:%s:%s\" \"${PMA_HTPASSWD_USERNAME}\" \"phpmyadmin\" \"${PMA_HTPASSWD_PASSWORD}\" | md5sum | awk '{print $$1}' ) > ${PMA_CONF_FOLDER}/.htpasswd 145 | && printf 'AuthType Digest\\nAuthName \"phpmyadmin\"\\nAuthDigestProvider file\\nAuthUserFile ${PMA_CONF_FOLDER}/.htpasswd\\nRequire valid-user\\n' > ${WEBSERVER_DOC_ROOT}/.htaccess && a2enmod auth_digest; 146 | mkdir -p ${WEBSERVER_DOC_ROOT}/../upload && chown www-data:www-data ${WEBSERVER_DOC_ROOT}/../upload && chmod a+w ${WEBSERVER_DOC_ROOT}/../upload; mkdir -p ${WEBSERVER_DOC_ROOT}/../save && chown www-data:www-data ${WEBSERVER_DOC_ROOT}/../save && chmod a+w ${WEBSERVER_DOC_ROOT}/../save; 147 | grep -qxF 'ServerName 127.0.0.1' ${APACHE_CONFDIR_PREFIX}/apache2.conf || echo -e '\\nServerName 127.0.0.1' >> ${APACHE_CONFDIR_PREFIX}/apache2.conf; grep -qe 'date.timezone = ${LOCAL_TIMEZONE}' ${PHP_INI_DIR_PREFIX}/php/conf.d/security.ini || echo 'date.timezone = ${LOCAL_TIMEZONE}' >> ${PHP_INI_DIR_PREFIX}/php/conf.d/security.ini; 148 | a2enmod ssl && a2ensite default-ssl && a2dissite 000-default && /docker-entrypoint.sh 'apache2-foreground'" 149 | 150 | database: 151 | image: ${DATABASE_IMAGE_NAME}:${DATABASE_VERSION} 152 | container_name: database 153 | networks: 154 | - backend 155 | volumes: 156 | - 'db:/var/lib/mysql' 157 | - 'db-backup-data:/tmp/backup' 158 | - type: bind 159 | source: ./database/conf.d/z-mysql.cnf 160 | target: '${MYSQL_CONF_PREFIX}/z-mysql.cnf' 161 | - 'phpmyadmin-sql:/docker-entrypoint-initdb.d' 162 | hostname: database 163 | restart: unless-stopped 164 | ports: 165 | - '3306:3306' 166 | healthcheck: 167 | test: ["CMD-SHELL", "${DATABASE_ADMIN_COMMANDLINE} ping --silent || exit 1"] 168 | interval: 5s 169 | timeout: 5s 170 | retries: 50 171 | environment: 172 | MYSQL_ROOT_PASSWORD: '${MYSQL_ROOT_PASSWORD}' 173 | MYSQL_DATABASE: '${WORDPRESS_DB_NAME}' 174 | MYSQL_USER: '${WORDPRESS_DB_USER}' 175 | MYSQL_PASSWORD: '${WORDPRESS_DB_PASSWORD}' 176 | MYSQL_ALLOW_EMPTY_PASSWORD: 'No' 177 | MYSQL_ROOT_HOST: '${MYSQL_ROOT_HOST}' 178 | TZ: '${LOCAL_TIMEZONE}' 179 | labels: 180 | - "docker-volume-backup.stop-during-backup=true" 181 | - "docker-volume-backup.archive-pre=/bin/sh -c 'mysqldump -uroot -p${MYSQL_ROOT_PASSWORD} --all-databases > /tmp/backup/db_backup_data.sql'" 182 | - "docker-volume-backup.exec-label=database" 183 | command: bash -c "${DATABASE_PACKAGE_MANAGER} && export PMA_CONTROLUSER=${PMA_CONTROLUSER} export PMA_CONTROLPASS=${PMA_CONTROLPASS} && envsubst '$$PMA_CONTROLUSER,$$PMA_CONTROLPASS' < /docker-entrypoint-initdb.d/create_tables.sql.template > /docker-entrypoint-initdb.d/create_tables.sql && docker-entrypoint.sh --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci" 184 | 185 | redis: 186 | image: redis:latest 187 | container_name: redis 188 | networks: 189 | - backend 190 | volumes: 191 | - 'dtredis:/data' 192 | - type: bind 193 | source: ./redis 194 | target: '${REDIS_CONF_PREFIX}/redis' 195 | hostname: redis 196 | sysctls: 197 | - net.core.somaxconn=512 198 | restart: unless-stopped 199 | ports: 200 | - '6379:6379' 201 | environment: 202 | ALLOW_EMPTY_PASSWORD: 'yes' 203 | TZ: '${LOCAL_TIMEZONE}' 204 | command: "redis-server ${REDIS_CONF_PREFIX}/redis/redis.conf" 205 | 206 | varnish: 207 | depends_on: 208 | wordpress: 209 | condition: service_healthy 210 | image: varnish:${VARNISH_VERSION} 211 | container_name: varnish 212 | networks: 213 | - backend 214 | volumes: 215 | - type: bind 216 | source: ./varnish/default.vcl 217 | target: '${VARNISH_CONF_PREFIX}/default.vcl' 218 | hostname: varnish 219 | tmpfs: 220 | - /var/lib/varnish:exec 221 | restart: unless-stopped 222 | ports: 223 | - '8080:80' 224 | environment: 225 | VARNISH_SIZE: '${VARNISH_SIZE}' 226 | TZ: '${LOCAL_TIMEZONE}' 227 | command: "-a http=:8080,HTTP -p default_ttl=3600 -n /tmp/varnish_workdir" 228 | 229 | backup: 230 | image: offen/docker-volume-backup:latest 231 | container_name: backup 232 | networks: 233 | - backend 234 | volumes: 235 | - 'html:/backup/html:ro' 236 | - 'db:/backup/db:ro' 237 | - 'db-backup-data:/backup/db-backup-data:ro' 238 | - '/var/run/docker.sock:/var/run/docker.sock:ro' 239 | - type: bind 240 | source: ./backups 241 | target: /archive 242 | hostname: backup 243 | restart: unless-stopped 244 | environment: 245 | BACKUP_CRON_EXPRESSION: '20 01 * * *' 246 | BACKUP_FILENAME: 'backup-%Y-%m-%dT%H-%M-%S.tar.gz' 247 | BACKUP_RETENTION_DAYS: '7' 248 | EXEC_LABEL: 'database' 249 | BACKUP_EXCLUDE_REGEXP: 'wp-admin|wp-includes|\\.log$$' 250 | 251 | networks: 252 | backend: null 253 | frontend: null 254 | 255 | volumes: 256 | html: 257 | name: wordpress-html 258 | driver: local 259 | driver_opts: 260 | type: none 261 | device: ${DIRECTORY_PATH}/wordpress 262 | o: bind 263 | db: 264 | name: ${DATABASE_CONT_NAME}-data 265 | db-backup-data: 266 | name: ${DATABASE_CONT_NAME}-backup-data 267 | phpmyadmin-sql: 268 | name: phpmyadmin-sql 269 | driver: local 270 | driver_opts: 271 | type: none 272 | device: ${DIRECTORY_PATH}/database/phpmyadmin/sql 273 | o: bind 274 | dtredis: 275 | name: redis-data 276 | certbot-etc: 277 | external: true 278 | certbot-var: 279 | name: certbot-var 280 | -------------------------------------------------------------------------------- /env.example: -------------------------------------------------------------------------------- 1 | WEBSERVER_DOC_ROOT=/var/www/html 2 | NGINX_PREFIX=/etc/nginx 3 | LOCAL_TIMEZONE=local_timezone 4 | DOMAIN_NAME=example.com 5 | DIRECTORY_PATH=directory_path 6 | 7 | LETSENCRYPT_EMAIL=email@domain.com 8 | LETSENCRYPT_CONF_PREFIX=/etc/letsencrypt 9 | SSL_SNIPPET=ssl_snippet 10 | 11 | PHP_IMAGE_NAME=php8.3-fpm 12 | PHP_INI_DIR_PREFIX=/usr/local/etc 13 | 14 | WORDPRESS_DB_USER=db_username 15 | WORDPRESS_DB_PASSWORD=db_password 16 | WORDPRESS_DB_NAME=db_name 17 | WORDPRESS_TABLE_PREFIX=db_table_prefix 18 | 19 | MYSQL_CONF_PREFIX=/etc/mysql/conf.d 20 | MYSQL_ROOT_PASSWORD=mysql_root_password 21 | MYSQL_ROOT_HOST=% 22 | DATABASE_IMAGE_NAME=which_db 23 | DATABASE_CONT_NAME=which_db 24 | DATABASE_VERSION=latest 25 | DATABASE_PACKAGE_MANAGER=db_package_manager 26 | DATABASE_ADMIN_COMMANDLINE=db_admin_commandline 27 | 28 | PMA_CONTROLUSER=pma_username 29 | PMA_CONTROLPASS=pma_password 30 | PMA_CONF_FOLDER=/etc/phpmyadmin 31 | PMA_HTPASSWD_USERNAME=db_username 32 | PMA_HTPASSWD_PASSWORD=db_password 33 | PMA_UPLOAD_LIMIT=8M 34 | PMA_MEMORY_LIMIT=-1 35 | APACHE_CONFDIR_PREFIX=/etc/apache2 36 | 37 | REDIS_CONF_PREFIX=/usr/local/etc 38 | 39 | VARNISH_VERSION=varnish_version 40 | VARNISH_CONF_PREFIX=/etc/varnish 41 | VARNISH_SIZE=2G 42 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | clear 4 | echo "" 5 | echo "=======================================================================" 6 | echo "| |" 7 | echo "| full-stack-nginx-wordpress-for-everyone-with-docker-compose |" 8 | echo "| by Erdal ALTIN |" 9 | echo "| |" 10 | echo "=======================================================================" 11 | sleep 2 12 | 13 | # the "lpms" is an abbreviation of Linux Package Management System 14 | lpms="" 15 | for i in apk dnf yum apt zypper pacman 16 | do 17 | if [ -x "$(command -v $i)" ]; then 18 | if [ "$i" == "apk" ] 19 | then 20 | lpms=$i 21 | sudo apk add --no-cache --upgrade grep 22 | break 23 | elif [ "$i" == "dnf" ] && ([[ $(grep -Pow 'ID=\K[^;]*' /etc/os-release | tr -d '"') == "fedora" ]] || (([[ $(grep -Pow 'ID=\K[^;]*' /etc/os-release | tr -d '"') != "centos" ]] && [[ $(grep -Pow 'ID_LIKE=\K[^;]*' /etc/os-release | tr -d '"') == *"fedora"* ]]) || ([[ $(grep -Pow 'ID_LIKE=\K[^;]*' /etc/os-release | tr -d '"') == *"rhel"* ]] && [ $(sudo uname -m) == "s390x" ]))) 24 | then 25 | lpms=$i 26 | break 27 | elif [ "$i" == "yum" ] && ([[ $(grep -Pow 'ID=\K[^;]*' /etc/os-release | tr -d '"') == "centos" ]] || (([[ $(grep -Pow 'ID=\K[^;]*' /etc/os-release | tr -d '"') != "fedora" ]] && [[ $(grep -Pow 'ID_LIKE=\K[^;]*' /etc/os-release | tr -d '"') == *"fedora"* ]]) || ([[ $(grep -Pow 'ID_LIKE=\K[^;]*' /etc/os-release | tr -d '"') == *"rhel"* ]] && [ $(sudo uname -m) == "s390x" ]))) 28 | then 29 | lpms=$i 30 | break 31 | elif [ "$i" == "apt" ] && ([[ $(grep -Pow 'ID=\K[^;]*' /etc/os-release | tr -d '"') == *"ubuntu"* ]] || [[ $(grep -Pow 'ID=\K[^;]*' /etc/os-release | tr -d '"') == *"debian"* ]] || [[ $(grep -Pow 'ID_LIKE=\K[^;]*' /etc/os-release | tr -d '"') == *"ubuntu"* ]] || [[ $(grep -Pow 'ID_LIKE=\K[^;]*' /etc/os-release | tr -d '"') == *"debian"* ]]) 32 | then 33 | lpms=$i 34 | break 35 | elif [[ $(grep -Pow 'ID_LIKE=\K[^;]*' /etc/os-release) == *"suse"* ]] 36 | then 37 | lpms=$i 38 | break 39 | elif [ "$i" == "pacman" ] 40 | then 41 | lpms=$i 42 | break 43 | fi 44 | fi 45 | done 46 | 47 | if [ -z $lpms ]; then 48 | echo "" 49 | echo "could not be detected package management system" 50 | echo "" 51 | exit 0 52 | fi 53 | 54 | ########## 55 | # set varnish version 56 | ########## 57 | varnish_version="stable" 58 | if ([[ $(grep -Pow 'VERSION_ID=\K[^;]*' /etc/os-release | tr -d '"') == 9* ]] && [ $(grep -Pow 'ID=\K[^;]*' /etc/os-release | tr -d '"') == "centos" ]) || [ $(grep -Pow 'ID=\K[^;]*' /etc/os-release | tr -d '"') == "fedora" ] 59 | then 60 | varnish_version="latest" 61 | fi 62 | 63 | ########## 64 | # Uninstall old versions 65 | ########## 66 | echo "" 67 | echo "" 68 | echo "=======================================================================" 69 | echo "| Older versions of Docker were called docker, docker.io, or docker-engine." 70 | echo "| If these are installed or all conflicting packages, uninstall them." 71 | echo "=======================================================================" 72 | echo "" 73 | sleep 2 74 | 75 | # linux remove command for pms 76 | if [ "$lpms" == "apk" ] 77 | then 78 | sudo apk del docker podman-docker 79 | elif [ "$lpms" == "dnf" ] 80 | then 81 | sudo dnf remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engine 82 | elif [ "$lpms" == "yum" ] 83 | then 84 | sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine podman runc 85 | elif [ "$lpms" == "apt" ] 86 | then 87 | for pkg in docker docker-engine docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt remove $pkg; done 88 | elif [ "$lpms" == "zypper" ] 89 | then 90 | if [[ $(grep -Pow 'ID=\K[^;]*' /etc/os-release | tr -d '"') == *"sles"* ]] 91 | then 92 | sudo zypper remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine runc 93 | fi 94 | elif [ "$lpms" == "pacman" ] 95 | then 96 | sudo pacman -Rssn podman-docker podman-compose 97 | else 98 | echo "" 99 | echo "could not be detected package management system" 100 | echo "" 101 | exit 0 102 | fi 103 | 104 | echo "" 105 | echo "Done ✓" 106 | echo "=======================================================================" 107 | 108 | ########## 109 | # Install Docker 110 | ########## 111 | echo "" 112 | echo "" 113 | echo "=======================================================================" 114 | echo "| Install Docker..." 115 | echo "=======================================================================" 116 | echo "" 117 | sleep 2 118 | 119 | if [ "$lpms" == "apk" ] 120 | then 121 | sudo apk add --update docker openrc bind-tools 122 | sudo rc-update add docker boot 123 | sudo service docker start 124 | elif [ "$lpms" == "dnf" ] 125 | then 126 | sudo dnf -y install dnf-plugins-core 127 | if [[ $(grep -Pow 'ID=\K[^;]*' /etc/os-release | tr -d '"') == "fedora" ]] || ([[ $(grep -Pow 'ID=\K[^;]*' /etc/os-release | tr -d '"') == "rhel" ]] && [ $(sudo uname -m) == "s390x" ]) 128 | then 129 | sudo dnf config-manager --add-repo https://download.docker.com/linux/$(grep -Pow 'ID=\K[^;]*' /etc/os-release | tr -d '"')/docker-ce.repo 130 | sudo dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin bind-utils 131 | elif [[ $(grep -Pow 'ID=\K[^;]*' /etc/os-release | tr -d '"') != "rhel" ]] 132 | then 133 | sudo dnf install docker 134 | else 135 | echo "" 136 | echo "unsupport operation system and/or architecture" 137 | echo "" 138 | exit 0 139 | fi 140 | elif [ "$lpms" == "yum" ] 141 | then 142 | sudo yum install -y yum-utils 143 | if [[ $(grep -Pow 'ID=\K[^;]*' /etc/os-release | tr -d '"') == "centos" ]] || ([[ $(grep -Pow 'ID=\K[^;]*' /etc/os-release | tr -d '"') == "rhel" ]] && [ $(sudo uname -m) == "s390x" ]) 144 | then 145 | sudo yum-config-manager --add-repo https://download.docker.com/linux/$(grep -Pow 'ID=\K[^;]*' /etc/os-release | tr -d '"')/docker-ce.repo 146 | sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin bind-utils 147 | elif [[ $(grep -Pow 'ID=\K[^;]*' /etc/os-release | tr -d '"') != "rhel" ]] 148 | then 149 | sudo yum install docker 150 | else 151 | echo "" 152 | echo "unsupport operation system and/or architecture" 153 | echo "" 154 | exit 0 155 | fi 156 | elif [ "$lpms" == "zypper" ] 157 | then 158 | if [[ $(grep -Pow 'ID=\K[^;]*' /etc/os-release | tr -d '"') == *"sles"* ]] && [ $(sudo uname -m) == "s390x" ] 159 | then 160 | # "https://download.opensuse.org/repositories/security:/SELinux/openSUSE_Factory/security:SELinux.repo" 161 | sudo zypper addrepo "https://download.opensuse.org/repositories/security/$(grep -Pow 'VERSION_ID=\K[^;]*' /etc/os-release | tr -d '"')/security.repo" 162 | sudo zypper addrepo https://download.docker.com/linux/sles/docker-ce.repo 163 | sudo zypper install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 164 | else 165 | sudo SUSEConnect -p sle-module-containers/$(sudo uname -s)/$(sudo uname -m) -r '' 166 | sudo zypper install docker 167 | fi 168 | 169 | #Installed=`sudo zypper search --installed-only -v docker | sed -n '6p' | cut -c 28-40` 170 | #Candidate=`sudo zypper info docker | sed -n '10p' | cut -c 18-` 171 | elif [ "$lpms" == "apt" ] 172 | then 173 | sudo apt update 174 | sudo apt install ca-certificates curl gnupg lsb-release 175 | sudo mkdir -m 0755 /etc/apt/keyrings 176 | sudo curl -fsSL https://download.docker.com/linux/$(grep -Pow 'ID=\K[^;]*' /etc/os-release | tr -d '"')/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg 177 | sudo chmod a+r /etc/apt/keyrings/docker.gpg 178 | # Add the repository to Apt sources: 179 | echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$(grep -Pow 'ID=\K[^;]*' /etc/os-release | tr -d '"') $(grep -Po 'VERSION_CODENAME=\K[^;]*' /etc/os-release) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null 180 | sudo apt update 181 | sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 182 | 183 | #Installed=`sudo apt-cache policy docker-ce | sed -n '2p' | cut -c 14-` 184 | #Candidate=`sudo apt-cache policy docker-ce | sed -n '3p' | cut -c 14-` 185 | elif [ "$lpms" == "pacman" ] 186 | then 187 | sudo pacman -Syu --noconfirm 188 | sudo pacman -Ss docker docker-buildx 189 | else 190 | echo "" 191 | echo "could not be detected package management system" 192 | echo "" 193 | exit 0 194 | fi 195 | 196 | #sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 197 | #if [[ "$Installed" != "$Candidate" ]]; then 198 | # sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 199 | #elif [[ "$Installed" == "$Candidate" ]]; then 200 | # echo "" 201 | # echo 'docker currently version already installed.' 202 | #fi 203 | 204 | if [ $? -ne 0 ] 205 | then 206 | exit 0 207 | fi 208 | 209 | if [ $lpms != "apk" ] 210 | then 211 | sudo systemctl enable docker.service 212 | sudo systemctl enable containerd.service 213 | sudo systemctl start docker 214 | fi 215 | 216 | echo "" 217 | echo "Done ✓" 218 | echo "=======================================================================" 219 | 220 | ########## 221 | # Run Docker without sudo rights 222 | ########## 223 | echo "" 224 | echo "" 225 | echo "=======================================================================" 226 | echo "| Running Docker without sudo rights..." 227 | echo "=======================================================================" 228 | echo "" 229 | sleep 2 230 | 231 | sudo groupadd docker 232 | sudo usermod -aG docker ${USER} 233 | # su - ${USER} & 234 | 235 | echo "" 236 | echo "Done ✓" 237 | echo "=======================================================================" 238 | 239 | ########## 240 | # Install Docker Compose 241 | ########## 242 | echo "" 243 | echo "" 244 | echo "=======================================================================" 245 | echo "| Installing Docker Compose v2.32.4..." 246 | echo "=======================================================================" 247 | echo "" 248 | sleep 2 249 | 250 | sudo mkdir -p /usr/local/lib/docker/cli-plugins 251 | sudo curl -SL "https://github.com/docker/compose/releases/download/v2.32.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/lib/docker/cli-plugins/docker-compose 252 | sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose 253 | 254 | echo "" 255 | echo "Done ✓" 256 | echo "=======================================================================" 257 | 258 | ########## 259 | # permission for Docker daemon socket 260 | ########## 261 | echo "" 262 | echo "" 263 | echo "=======================================================================" 264 | echo "| permission for Docker daemon socket..." 265 | echo "=======================================================================" 266 | echo "" 267 | sleep 2 268 | 269 | sudo chmod 666 /var/run/docker.sock 270 | 271 | echo "" 272 | echo "Done ✓" 273 | echo "=======================================================================" 274 | 275 | clear 276 | ########## 277 | # Setup project variables 278 | ########## 279 | echo "" 280 | echo "=======================================================================" 281 | echo "| Please enter project related variables..." 282 | echo "=======================================================================" 283 | echo "" 284 | sleep 2 285 | 286 | # set the host 287 | which_h="" 288 | items=("localhost" "remotehost") 289 | PS3="which computer command line are you on? Select the host: " 290 | select h in "${items[@]}" 291 | do 292 | case $REPLY in 293 | 1) 294 | which_h=$h 295 | break;; 296 | 2) 297 | which_h=$h 298 | break;; 299 | *) 300 | echo "Invalid choice $REPLY";; 301 | esac 302 | done 303 | echo "Ok." 304 | 305 | # set your domain name 306 | if [ "$which_h" == "localhost" ] 307 | then 308 | read -p 'Enter Domain Name(default : localhost or e.g. : example.com): ' domain_name 309 | : ${domain_name:=localhost} 310 | [ "$domain_name" != "localhost" ] && sudo -- sh -c -e "grep -Eq '$domain_name' /etc/hosts || echo '127.0.0.1 $domain_name' >> /etc/hosts" 311 | else 312 | domain_name="" 313 | read -p 'Enter Domain Name(e.g. : example.com): ' domain_name 314 | #[ "$domain_name" != "localhost" ] && sudo -- sh -c -e "sed -i '/$domain_name/d' /etc/hosts" 315 | fi 316 | [ -z $domain_name ] && domain_name="NULL" 317 | host -N 0 $domain_name 2>&1 > /dev/null 318 | while [ $? -ne 0 ] 319 | do 320 | echo "Try again" 321 | sudo -- sh -c -e "sed -i '/$domain_name/d' /etc/hosts" 322 | if [ "$which_h" == "localhost" ] 323 | then 324 | read -p 'Enter Domain Name(default : localhost or e.g. : example.com): ' domain_name 325 | : ${domain_name:=localhost} 326 | [ "$domain_name" != "localhost" ] && sudo -- sh -c -e "grep -Eq '$domain_name' /etc/hosts || echo '127.0.0.1 $domain_name' >> /etc/hosts" 327 | else 328 | read -p 'Enter Domain Name(e.g. : example.com): ' domain_name 329 | #[ "$domain_name" != "localhost" ] && sudo -- sh -c -e "sed -i '/$domain_name/d' /etc/hosts" 330 | fi 331 | [ -z $domain_name ] && domain_name="NULL" 332 | host -N 0 $domain_name 2>&1 > /dev/null 333 | done 334 | echo "Ok." 335 | 336 | ssl_snippet="" 337 | if [ "$which_h" == "localhost" ] 338 | then 339 | ssl_snippet="echo 'Generated Self-signed SSL Certificate for localhost'" 340 | if [ "$lpms" == "apk" ] 341 | then 342 | sudo apk add --no-cache nss-tools go git 343 | elif [ "$lpms" == "dnf" ] 344 | then 345 | sudo dnf install nss-tools go git 346 | elif [ "$lpms" == "yum" ] 347 | then 348 | sudo yum install nss-tools go git 349 | elif [ "$lpms" == "zypper" ] 350 | then 351 | sudo zypper install mozilla-nss-tools go git 352 | elif [ "$lpms" == "apt" ] 353 | then 354 | sudo apt install libnss3-tools go git 355 | elif [ "$lpms" == "pacman" ] 356 | then 357 | sudo pacman -S nss go git 358 | else 359 | echo "" 360 | echo "could not be detected package management system" 361 | echo "" 362 | exit 0 363 | fi 364 | sudo rm -Rf mkcert && git clone https://github.com/FiloSottile/mkcert && cd mkcert && go build -ldflags "-X main.Version=$(git describe --tags)" 365 | sudo mkcert -uninstall && mkcert -install && mkcert -key-file privkey.pem -cert-file chain.pem $domain_name *.$domain_name && sudo cat privkey.pem chain.pem > fullchain.pem && sudo mkdir -p ../certbot/live/$domain_name && sudo mv *.pem ../certbot/live/$domain_name && cd .. 366 | echo "Ok." 367 | else 368 | ssl_snippet="certbot certonly --webroot --webroot-path \/tmp\/acme-challenge --rsa-key-size 4096 --non-interactive --agree-tos --no-eff-email --force-renewal --email \$\{LETSENCRYPT_EMAIL\} -d \$\{DOMAIN_NAME\} -d www.\$\{DOMAIN_NAME\}" 369 | fi 370 | 371 | # set parameters in env.example file 372 | email="" 373 | regex="^[a-zA-Z0-9\._-]+\@[a-zA-Z0-9._-]+\.[a-zA-Z]+\$" 374 | read -p 'Enter Email Address for letsencrypt ssl(e.g. : email@domain.com): ' email 375 | while [ -z $email ] || [[ ! $email =~ $regex ]] 376 | do 377 | echo "Try again" 378 | read -p 'Enter Email Address for letsencrypt ssl(e.g. : email@domain.com): ' email 379 | sleep 1 380 | done 381 | echo "Ok." 382 | 383 | db_username="" 384 | db_regex="^[0-9a-zA-Z\$_]{6,}$" 385 | read -p 'Enter Database Username(at least 6 characters): ' db_username 386 | while [[ ! $db_username =~ $db_regex ]] 387 | do 388 | echo "Try again (can only contain numerals 0-9, basic Latin letters, both lowercase and uppercase, dollar sign and underscore)" 389 | read -p 'Enter Database Username(at least 6 characters): ' db_username 390 | sleep 1 391 | done 392 | echo "Ok." 393 | 394 | db_password="" 395 | password_regex="^[a-zA-Z0-9\._-]{6,}$" 396 | read -p 'Enter Database Password(at least 6 characters): ' db_password 397 | while [[ ! $db_password =~ $password_regex ]] 398 | do 399 | echo "Try again (can only contain numerals 0-9, basic Latin letters, both lowercase and uppercase, dot, underscore and minus sign)" 400 | read -p 'Enter Database Password(at least 6 characters): ' db_password 401 | sleep 1 402 | done 403 | echo "Ok." 404 | 405 | db_name="" 406 | read -p 'Enter Database Name(at least 6 characters): ' db_name 407 | while [[ ! $db_name =~ $db_regex ]] 408 | do 409 | echo "Try again (can only contain numerals 0-9, basic Latin letters, both lowercase and uppercase, dollar sign and underscore)" 410 | read -p 'Enter Database Name(at least 6 characters): ' db_name 411 | sleep 1 412 | done 413 | echo "Ok." 414 | 415 | db_table_prefix_regex="^[0-9a-zA-Z\$_]{3,}$" 416 | read -p 'Enter Database Table Prefix(at least 3 characters, default : wp_): ' db_table_prefix 417 | : ${db_table_prefix:=wp_} 418 | while [[ ! $db_table_prefix =~ $db_table_prefix_regex ]] 419 | do 420 | echo "Try again (can only contain numerals 0-9, basic Latin letters, both lowercase and uppercase, dollar sign and underscore)" 421 | read -p 'Enter Database Table Prefix(at least 3 characters, default : wp_): ' db_table_prefix 422 | : ${db_table_prefix:=wp_} 423 | sleep 1 424 | done 425 | echo "Ok." 426 | 427 | mysql_root_password="" 428 | read -p 'Enter MariaDb/Mysql Root Password(at least 6 characters): ' mysql_root_password 429 | while [[ ! $mysql_root_password =~ $password_regex ]] 430 | do 431 | echo "Try again (can only contain numerals 0-9, basic Latin letters, both lowercase and uppercase, dot, underscore and minus sign)" 432 | read -p 'Enter MariaDb/Mysql Root Password(at least 6 characters): ' mysql_root_password 433 | sleep 1 434 | done 435 | echo "Ok." 436 | 437 | pma_username="" 438 | read -p 'Enter PhpMyAdmin Username(at least 6 characters): ' pma_username 439 | while [[ ! $pma_username =~ $db_regex ]] 440 | do 441 | echo "Try again (can only contain numerals 0-9, basic Latin letters, both lowercase and uppercase, dollar sign and underscore)" 442 | read -p 'Enter PhpMyAdmin Username(at least 6 characters): ' pma_username 443 | sleep 1 444 | done 445 | echo "Ok." 446 | 447 | pma_password="" 448 | read -p 'Enter PhpMyAdmin Password(at least 6 characters): ' pma_password 449 | while [[ ! $pma_password =~ $password_regex ]] 450 | do 451 | echo "Try again (can only contain numerals 0-9, basic Latin letters, both lowercase and uppercase, dot, underscore and minus sign)" 452 | read -p 'Enter PhpMyAdmin Password(at least 6 characters): ' pma_password 453 | sleep 1 454 | done 455 | echo "Ok." 456 | 457 | which_db="" 458 | db_authentication_password=$pma_password 459 | db_package_manager="apt-get update \&\& apt-get install -y gettext-base" 460 | db_admin_commandline="mariadb-admin" 461 | PS3="Select the database: " 462 | select db in mariadb mysql 463 | do 464 | which_db=$db 465 | if [ $REPLY -eq 2 ] 466 | then 467 | db_package_manager="microdnf install -y gettext" 468 | db_admin_commandline="mysqladmin" 469 | fi 470 | if [ $REPLY -eq 1 ] || [ $REPLY -eq 2 ] 471 | then 472 | break 473 | else 474 | PS3="Select the database: " 475 | fi 476 | done 477 | echo "Ok." 478 | 479 | local_timezone_regex="^[a-zA-Z0-9/+_-]{1,}$" 480 | read -p 'Enter container local Timezone(default : America/Los_Angeles, to see the other timezones, https://docs.diladele.com/docker/timezones.html): ' local_timezone 481 | : ${local_timezone:=America/Los_Angeles} 482 | while [[ ! $local_timezone =~ $local_timezone_regex ]] 483 | do 484 | echo "Try again (can only contain numerals 0-9, basic Latin letters, both lowercase and uppercase, positive, minus sign and underscore)" 485 | read -p 'Enter container local Timezone(default : America/Los_Angeles, to see the other local timezones, https://docs.diladele.com/docker/timezones.html): ' local_timezone 486 | sleep 1 487 | : ${local_timezone:=America/Los_Angeles} 488 | done 489 | local_timezone=${local_timezone//[\/]/\\\/} 490 | echo "Ok." 491 | 492 | read -p "Apply changes (y/n)? " choice 493 | case "$choice" in 494 | y|Y ) clear; echo ""; echo "Yes! Proceeding now...";; 495 | n|N ) echo "No! Aborting now..."; exit 0;; 496 | * ) echo "Invalid input! Aborting now..."; exit 0;; 497 | esac 498 | 499 | \cp ./phpmyadmin/apache2/sites-available/default-ssl.sample.conf ./phpmyadmin/apache2/sites-available/default-ssl.conf 500 | \cp ./database/phpmyadmin/sql/create_tables.sql.template.example ./database/phpmyadmin/sql/create_tables.sql.template 501 | 502 | \cp env.example .env 503 | 504 | sed -i "s/db_authentication_password/${db_authentication_password}/" ./database/phpmyadmin/sql/create_tables.sql.template 505 | sed -i "s|db_package_manager|${db_package_manager}|" .env 506 | sed -i 's/db_admin_commandline/'$db_admin_commandline'/' .env 507 | sed -i 's/example.com/'$domain_name'/' .env 508 | sed -i 's/example.com/'$domain_name'/g' ./phpmyadmin/apache2/sites-available/default-ssl.conf 509 | sed -i 's/email@domain.com/'$email'/' .env 510 | sed -i "s/ssl_snippet/$ssl_snippet/" .env 511 | sed -i 's/which_db/'$which_db'/g' .env 512 | sed -i 's/db_username/'$db_username'/g' .env 513 | sed -i 's/db_password/'$db_password'/g' .env 514 | sed -i 's/db_name/'$db_name'/' .env 515 | sed -i 's/db_table_prefix/'$db_table_prefix'/' .env 516 | sed -i 's/mysql_root_password/'$mysql_root_password'/' .env 517 | sed -i 's/pma_username/'$pma_username'/' .env 518 | sed -i 's/pma_password/'$pma_password'/' .env 519 | sed -i 's/pma_controluser/'$pma_username'/g' ./database/phpmyadmin/sql/create_tables.sql.template 520 | sed -i "s@directory_path@$(pwd)@" .env 521 | sed -i 's/local_timezone/'$local_timezone'/' .env 522 | sed -i 's/varnish_version/'$varnish_version'/' .env 523 | 524 | if [ -x "$(command -v docker)" ] && [ "$(docker compose version)" ]; then 525 | # Firstly: create external volume 526 | docker volume create --driver local --opt type=none --opt device=`pwd`/certbot --opt o=bind certbot-etc > /dev/null 527 | # installing WordPress and the other services 528 | docker compose up -d & export pid=$! 529 | echo "WordPress and the other services installing proceeding..." 530 | echo "" 531 | wait $pid 532 | if [ $? -eq 0 ] 533 | then 534 | # installing portainer 535 | docker compose -f portainer-docker-compose.yml -p portainer up -d & export pid=$! 536 | echo "" 537 | echo "portainer installing proceeding..." 538 | wait $pid 539 | if [ $? -ne 0 ]; then 540 | echo "Error! could not installed portainer" >&2 541 | exit 1 542 | else 543 | echo "" 544 | until [ -n "$(sudo find ./certbot/live -name '$domain_name' 2>/dev/null | head -1)" ]; do 545 | echo "waiting for Let's Encrypt certificates for $domain_name" 546 | sleep 5s & wait ${!} 547 | if sudo [ -d "./certbot/live/$domain_name" ]; then break; fi 548 | done 549 | echo "Ok." 550 | #until [ ! -z `docker compose ps -a --filter "status=running" --services | grep webserver` ]; do 551 | # echo "waiting starting webserver container" 552 | # sleep 2s & wait ${!} 553 | # if [ ! -z `docker compose ps -a --filter "status=running" --services | grep webserver` ]; then break; fi 554 | #done 555 | echo "" 556 | echo "Reloading webserver ssl configuration" 557 | docker container restart webserver > /dev/null 2>&1 558 | echo "Ok." 559 | echo "" 560 | echo "completed setup" 561 | echo "" 562 | echo "Website: https://$domain_name" 563 | echo "Portainer: https://$domain_name:9001" 564 | echo "phpMyAdmin: https://$domain_name:9090" 565 | echo "" 566 | echo "Ok." 567 | fi 568 | else 569 | echo "" 570 | echo "Error! could not installed WordPress and the other services with docker compose" >&2 571 | echo "" 572 | exit 1 573 | fi 574 | else 575 | echo "" 576 | echo "not found docker and/or docker compose, Install docker and/or docker compose" >&2 577 | echo "" 578 | exit 1 579 | fi 580 | -------------------------------------------------------------------------------- /php-fpm/php-fpm.d/z-www.conf: -------------------------------------------------------------------------------- 1 | [www] 2 | php_flag[display_errors] = off 3 | 4 | -------------------------------------------------------------------------------- /php-fpm/php/conf.d/security.ini: -------------------------------------------------------------------------------- 1 | max_execution_time = 60 2 | memory_limit = 3072M 3 | post_max_size = 8M 4 | upload_max_filesize = 8M 5 | max_input_time = 60 6 | file_uploads = On 7 | safe_mode = Off 8 | max_input_vars = 1000 9 | cgi.fix_pathinfo = 0 10 | -------------------------------------------------------------------------------- /phpmyadmin/apache2/ports.conf: -------------------------------------------------------------------------------- 1 | # If you just change the port or add more ports here, you will likely also 2 | # have to change the VirtualHost statement in 3 | # /etc/apache2/sites-enabled/000-default.conf 4 | 5 | # Listen 80 6 | 7 | 8 | Listen 443 9 | 10 | 11 | 12 | Listen 443 13 | 14 | 15 | # vim: syntax=apache ts=4 sw=4 sts=4 sr noet 16 | -------------------------------------------------------------------------------- /phpmyadmin/apache2/sites-available/000-default.conf: -------------------------------------------------------------------------------- 1 | 2 | # The ServerName directive sets the request scheme, hostname and port that 3 | # the server uses to identify itself. This is used when creating 4 | # redirection URLs. In the context of virtual hosts, the ServerName 5 | # specifies what hostname must appear in the request's Host: header to 6 | # match this virtual host. For the default virtual host (this file) this 7 | # value is not decisive as it is used as a last resort host regardless. 8 | # However, you must set it for any further virtual host explicitly. 9 | ServerName example.com 10 | 11 | ServerAdmin webmaster@localhost 12 | DocumentRoot /var/www/html 13 | 14 | # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, 15 | # error, crit, alert, emerg. 16 | # It is also possible to configure the loglevel for particular 17 | # modules, e.g. 18 | #LogLevel info ssl:warn 19 | 20 | ErrorLog ${APACHE_LOG_DIR}/error.log 21 | CustomLog ${APACHE_LOG_DIR}/access.log combined 22 | 23 | # For most configuration files from conf-available/, which are 24 | # enabled or disabled at a global level, it is possible to 25 | # include a line for only one particular virtual host. For example the 26 | # following line enables the CGI configuration for this host only 27 | # after it has been globally disabled with "a2disconf". 28 | #Include conf-available/serve-cgi-bin.conf 29 | 30 | 31 | # vim: syntax=apache ts=4 sw=4 sts=4 sr noet 32 | -------------------------------------------------------------------------------- /phpmyadmin/apache2/sites-available/default-ssl.sample.conf: -------------------------------------------------------------------------------- 1 | 2 | 3 | ServerAdmin info@example.com 4 | ServerName example.com 5 | ServerAlias *.example.com 6 | 7 | Protocols h2 http/1.1 8 | 9 | DocumentRoot /var/www/html 10 | 11 | # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, 12 | # error, crit, alert, emerg. 13 | # It is also possible to configure the loglevel for particular 14 | # modules, e.g. 15 | #LogLevel info ssl:warn 16 | 17 | ErrorLog ${APACHE_LOG_DIR}/error.log 18 | CustomLog ${APACHE_LOG_DIR}/access.log combined 19 | 20 | # For most configuration files from conf-available/, which are 21 | # enabled or disabled at a global level, it is possible to 22 | # include a line for only one particular virtual host. For example the 23 | # following line enables the CGI configuration for this host only 24 | # after it has been globally disabled with "a2disconf". 25 | #Include conf-available/serve-cgi-bin.conf 26 | 27 | # SSL Engine Switch: 28 | # Enable/Disable SSL for this virtual host. 29 | SSLEngine on 30 | 31 | # A self-signed (snakeoil) certificate can be created by installing 32 | # the ssl-cert package. See 33 | # /usr/share/doc/apache2/README.Debian.gz for more info. 34 | # If both key and certificate are stored in the same file, only the 35 | # SSLCertificateFile directive is needed. 36 | SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem 37 | SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem 38 | 39 | # Server Certificate Chain: 40 | # Point SSLCertificateChainFile at a file containing the 41 | # concatenation of PEM encoded CA certificates which form the 42 | # certificate chain for the server certificate. Alternatively 43 | # the referenced file can be the same as SSLCertificateFile 44 | # when the CA certificates are directly appended to the server 45 | # certificate for convinience. 46 | SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem 47 | 48 | # Certificate Authority (CA): 49 | # Set the CA certificate verification path where to find CA 50 | # certificates for client authentication or alternatively one 51 | # huge file containing all of them (file must be PEM encoded) 52 | # Note: Inside SSLCACertificatePath you need hash symlinks 53 | # to point to the certificate files. Use the provided 54 | # Makefile to update the hash symlinks after changes. 55 | #SSLCACertificatePath /etc/ssl/certs/ 56 | #SSLCACertificateFile /etc/apache2/ssl.crt/ca-bundle.crt 57 | 58 | # Certificate Revocation Lists (CRL): 59 | # Set the CA revocation path where to find CA CRLs for client 60 | # authentication or alternatively one huge file containing all 61 | # of them (file must be PEM encoded) 62 | # Note: Inside SSLCARevocationPath you need hash symlinks 63 | # to point to the certificate files. Use the provided 64 | # Makefile to update the hash symlinks after changes. 65 | #SSLCARevocationPath /etc/apache2/ssl.crl/ 66 | #SSLCARevocationFile /etc/apache2/ssl.crl/ca-bundle.crl 67 | 68 | # Client Authentication (Type): 69 | # Client certificate verification type and depth. Types are 70 | # none, optional, require and optional_no_ca. Depth is a 71 | # number which specifies how deeply to verify the certificate 72 | # issuer chain before deciding the certificate is not valid. 73 | #SSLVerifyClient require 74 | #SSLVerifyDepth 10 75 | 76 | # SSL Engine Options: 77 | # Set various options for the SSL engine. 78 | # o FakeBasicAuth: 79 | # Translate the client X.509 into a Basic Authorisation. This means that 80 | # the standard Auth/DBMAuth methods can be used for access control. The 81 | # user name is the `one line' version of the client's X.509 certificate. 82 | # Note that no password is obtained from the user. Every entry in the user 83 | # file needs this password: `xxj31ZMTZzkVA'. 84 | # o ExportCertData: 85 | # This exports two additional environment variables: SSL_CLIENT_CERT and 86 | # SSL_SERVER_CERT. These contain the PEM-encoded certificates of the 87 | # server (always existing) and the client (only existing when client 88 | # authentication is used). This can be used to import the certificates 89 | # into CGI scripts. 90 | # o StdEnvVars: 91 | # This exports the standard SSL/TLS related `SSL_*' environment variables. 92 | # Per default this exportation is switched off for performance reasons, 93 | # because the extraction step is an expensive operation and is usually 94 | # useless for serving static content. So one usually enables the 95 | # exportation for CGI and SSI requests only. 96 | # o OptRenegotiate: 97 | # This enables optimized SSL connection renegotiation handling when SSL 98 | # directives are used in per-directory context. 99 | SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire 100 | 101 | SSLOptions +StdEnvVars 102 | 103 | 104 | SSLOptions +StdEnvVars 105 | 106 | 107 | # SSL Protocol Adjustments: 108 | # The safe and default but still SSL/TLS standard compliant shutdown 109 | # approach is that mod_ssl sends the close notify alert but doesn't wait for 110 | # the close notify alert from client. When you need a different shutdown 111 | # approach you can use one of the following variables: 112 | # o ssl-unclean-shutdown: 113 | # This forces an unclean shutdown when the connection is closed, i.e. no 114 | # SSL close notify alert is send or allowed to received. This violates 115 | # the SSL/TLS standard but is needed for some brain-dead browsers. Use 116 | # this when you receive I/O errors because of the standard approach where 117 | # mod_ssl sends the close notify alert. 118 | # o ssl-accurate-shutdown: 119 | # This forces an accurate shutdown when the connection is closed, i.e. a 120 | # SSL close notify alert is send and mod_ssl waits for the close notify 121 | # alert of the client. This is 100% SSL/TLS standard compliant, but in 122 | # practice often causes hanging connections with brain-dead browsers. Use 123 | # this only for browsers where you know that their SSL implementation 124 | # works correctly. 125 | # Notice: Most problems of broken clients are also related to the HTTP 126 | # keep-alive facility, so you usually additionally want to disable 127 | # keep-alive for those clients, too. Use variable "nokeepalive" for this. 128 | # Similarly, one has to force some clients to use HTTP/1.0 to workaround 129 | # their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and 130 | # "force-response-1.0" for this. 131 | # BrowserMatch "MSIE [2-6]" \ 132 | # nokeepalive ssl-unclean-shutdown \ 133 | # downgrade-1.0 force-response-1.0 134 | 135 | Include /etc/letsencrypt/options-ssl-apache.conf 136 | 137 | 138 | 139 | 140 | # vim: syntax=apache ts=4 sw=4 sts=4 sr noet 141 | -------------------------------------------------------------------------------- /phpmyadmin/config.user.inc.php: -------------------------------------------------------------------------------- 1 | /dev/null ]; then 10 | openssl dhparam -out $2/ssl-dhparam.pem 2048 11 | fi 12 | 13 | use_lets_encrypt_certificates() { 14 | echo "switching webserver to use Let's Encrypt certificate for $1" 15 | sed '/#location.\/./,/#}/ s/#//; s/#listen/listen/g; s/#ssl_/ssl_/g' $3/conf.d/default.conf > $3/conf.d/default.conf.bak 16 | } 17 | 18 | reload_webserver() { 19 | cp $1/conf.d/default.conf.bak $1/conf.d/default.conf 20 | rm $1/conf.d/default.conf.bak 21 | echo "Starting webserver nginx service" 22 | nginx -t 23 | } 24 | 25 | wait_for_lets_encrypt() { 26 | if [ -d "$2/live/$1" ]; then 27 | break 28 | else 29 | until [ -d "$2/live/$1" ]; do 30 | echo "waiting for Let's Encrypt certificates for $1" 31 | sleep 5s & wait ${!} 32 | if [ -d "$2/live/$1" ]; then break; fi 33 | done 34 | fi; 35 | use_lets_encrypt_certificates "$1" "$2" "$3" 36 | reload_webserver "$3" 37 | } 38 | 39 | if [ ! -d "$2/live/$1" ]; then 40 | wait_for_lets_encrypt "$1" "$2" "$3" & 41 | else 42 | use_lets_encrypt_certificates "$1" "$2" "$3" 43 | reload_webserver "$3" 44 | fi 45 | 46 | nginx -g 'daemon off;' 47 | -------------------------------------------------------------------------------- /varnish/default.vcl: -------------------------------------------------------------------------------- 1 | vcl 4.1; 2 | 3 | import std; 4 | 5 | backend default { 6 | .host = "webserver"; 7 | .port = "90"; 8 | .connect_timeout = 2s; 9 | } 10 | 11 | # Add hostnames, IP addresses and subnets that are allowed to purge content 12 | acl purge { 13 | "webserver"; 14 | "wordpress"; 15 | "localhost"; 16 | "127.0.0.1"; 17 | "::1"; 18 | } 19 | 20 | sub vcl_recv { 21 | # Remove empty query string parameters 22 | # e.g.: www.example.com/index.html? 23 | if (req.url ~ "\?$") { 24 | set req.url = regsub(req.url, "\?$", ""); 25 | } 26 | 27 | # Remove port number from host header 28 | set req.http.Host = regsub(req.http.Host, ":[0-9]+", ""); 29 | 30 | # Sorts query string parameters alphabetically for cache normalization purposes 31 | set req.url = std.querysort(req.url); 32 | 33 | # Remove the proxy header to mitigate the httpoxy vulnerability 34 | # See https://httpoxy.org/ 35 | unset req.http.proxy; 36 | 37 | # Purge logic to remove objects from the cache. 38 | # Tailored to the Proxy Cache Purge WordPress plugin 39 | # See https://wordpress.org/plugins/varnish-http-purge/ 40 | if(req.method == "PURGE") { 41 | if(!client.ip ~ purge) { 42 | return(synth(405,"PURGE not allowed for this IP address")); 43 | } 44 | if (req.http.X-Purge-Method == "regex") { 45 | ban("obj.http.x-url ~ " + req.url + " && obj.http.x-host == " + req.http.host); 46 | return(synth(200, "Purged")); 47 | } 48 | ban("obj.http.x-url == " + req.url + " && obj.http.x-host == " + req.http.host); 49 | return(synth(200, "Purged")); 50 | } 51 | 52 | # Only handle relevant HTTP request methods 53 | if ( 54 | req.method != "GET" && 55 | req.method != "HEAD" && 56 | req.method != "PUT" && 57 | req.method != "POST" && 58 | req.method != "PATCH" && 59 | req.method != "TRACE" && 60 | req.method != "OPTIONS" && 61 | req.method != "DELETE" 62 | ) { 63 | return (pipe); 64 | } 65 | 66 | # Remove tracking query string parameters used by analytics tools 67 | if (req.url ~ "(\?|&)(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=") { 68 | set req.url = regsuball(req.url, "&(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", ""); 69 | set req.url = regsuball(req.url, "\?(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "?"); 70 | set req.url = regsub(req.url, "\?&", "?"); 71 | set req.url = regsub(req.url, "\?$", ""); 72 | } 73 | 74 | # Only cache GET and HEAD requests 75 | if (req.method != "GET" && req.method != "HEAD") { 76 | set req.http.X-Cacheable = "NO:REQUEST-METHOD"; 77 | return(pass); 78 | } 79 | 80 | # Mark static files with the X-Static-File header, and remove any cookies 81 | # X-Static-File is also used in vcl_backend_response to identify static files 82 | if (req.url ~ "^[^?]*\.(7z|avi|bmp|bz2|css|csv|doc|docx|eot|flac|flv|gif|gz|ico|jpeg|jpg|js|less|mka|mkv|mov|mp3|mp4|mpeg|mpg|odt|ogg|ogm|opus|otf|pdf|png|ppt|pptx|rar|rtf|svg|svgz|swf|tar|tbz|tgz|ttf|txt|txz|wav|webm|webp|woff|woff2|xls|xlsx|xml|xz|zip)(\?.*)?$") { 83 | set req.http.X-Static-File = "true"; 84 | unset req.http.Cookie; 85 | return(hash); 86 | } 87 | 88 | # No caching of special URLs, logged in users and some plugins 89 | if ( 90 | req.http.Cookie ~ "wordpress_(?!test_)[a-zA-Z0-9_]+|wp-postpass|comment_author_[a-zA-Z0-9_]+|woocommerce_cart_hash|woocommerce_items_in_cart|wp_woocommerce_session_[a-zA-Z0-9]+|wordpress_logged_in_|comment_author|PHPSESSID" || 91 | req.http.Authorization || 92 | req.url ~ "add_to_cart" || 93 | req.url ~ "edd_action" || 94 | req.url ~ "nocache" || 95 | req.url ~ "^/addons" || 96 | req.url ~ "^/bb-admin" || 97 | req.url ~ "^/bb-login.php" || 98 | req.url ~ "^/bb-reset-password.php" || 99 | req.url ~ "^/cart" || 100 | req.url ~ "^/checkout" || 101 | req.url ~ "^/control.php" || 102 | req.url ~ "^/login" || 103 | req.url ~ "^/logout" || 104 | req.url ~ "^/lost-password" || 105 | req.url ~ "^/my-account" || 106 | req.url ~ "^/product" || 107 | req.url ~ "^/register" || 108 | req.url ~ "^/register.php" || 109 | req.url ~ "^/server-status" || 110 | req.url ~ "^/signin" || 111 | req.url ~ "^/signup" || 112 | req.url ~ "^/stats" || 113 | req.url ~ "^/wc-api" || 114 | req.url ~ "^/wp-admin" || 115 | req.url ~ "^/wp-comments-post.php" || 116 | req.url ~ "^/wp-cron.php" || 117 | req.url ~ "^/wp-login.php" || 118 | req.url ~ "^/wp-activate.php" || 119 | req.url ~ "^/wp-mail.php" || 120 | req.url ~ "^/wp-login.php" || 121 | req.url ~ "^\?add-to-cart=" || 122 | req.url ~ "^\?wc-api=" || 123 | req.url ~ "^/preview=" || 124 | req.url ~ "^/\.well-known/acme-challenge/" 125 | ) { 126 | set req.http.X-Cacheable = "NO:Logged in/Got Sessions"; 127 | if(req.http.X-Requested-With == "XMLHttpRequest") { 128 | set req.http.X-Cacheable = "NO:Ajax"; 129 | } 130 | return(pass); 131 | } 132 | 133 | # Remove x-cache-status header 134 | unset req.http.x-cache-status; 135 | 136 | # Remove any cookies left 137 | unset req.http.Cookie; 138 | return(hash); 139 | } 140 | 141 | sub vcl_hash { 142 | if(req.http.X-Forwarded-Proto) { 143 | # Create cache variations depending on the request protocol 144 | hash_data(req.http.X-Forwarded-Proto); 145 | } 146 | } 147 | 148 | sub vcl_hit { 149 | set req.http.x-cache-status = "HIT"; 150 | if (obj.ttl <= 0s && obj.grace > 0s) { 151 | set req.http.x-cache-status = "HIT graced"; 152 | } 153 | 154 | if (req.method == "PURGE") { 155 | return(synth(200, "OK")); 156 | } 157 | } 158 | 159 | sub vcl_miss { 160 | set req.http.x-cache-status = "MISS"; 161 | 162 | if (req.method == "PURGE") { 163 | return(synth(404, "Not cached")); 164 | } 165 | } 166 | 167 | sub vcl_pass { 168 | set req.http.x-cache-status = "PASS"; 169 | } 170 | 171 | sub vcl_pipe { 172 | set req.http.x-cache-status = "pipe uncacheable"; 173 | } 174 | 175 | sub vcl_synth { 176 | set req.http.x-cache-status = "synth synth"; 177 | # uncomment the following line to show the information in the response 178 | set resp.http.x-cache-status = req.http.x-cache-status; 179 | 180 | # redirect for http 181 | if (resp.status == 750) { 182 | set resp.status = 301; 183 | set resp.http.Location = req.http.x-redir; 184 | return(deliver); 185 | } 186 | } 187 | 188 | sub vcl_backend_response { 189 | # Inject URL & Host header into the object for asynchronous banning purposes 190 | set beresp.http.x-url = bereq.url; 191 | set beresp.http.x-host = bereq.http.host; 192 | 193 | # If we dont get a Cache-Control header from the backend 194 | # we default to 1h cache for all objects 195 | if (!beresp.http.Cache-Control) { 196 | set beresp.ttl = 1h; 197 | set beresp.http.X-Cacheable = "YES:Forced"; 198 | } 199 | 200 | # If the file is marked as static we cache it for 1 day 201 | if (bereq.http.X-Static-File == "true") { 202 | unset beresp.http.Set-Cookie; 203 | set beresp.http.X-Cacheable = "YES:Forced"; 204 | set beresp.ttl = 1d; 205 | } 206 | 207 | # Remove the Set-Cookie header when a specific Wordfence cookie is set 208 | if (beresp.http.Set-Cookie ~ "wfvt_|wordfence_verifiedHuman") { 209 | unset beresp.http.Set-Cookie; 210 | } 211 | 212 | if (beresp.http.Set-Cookie) { 213 | set beresp.http.X-Cacheable = "NO:Got Cookies"; 214 | } elseif(beresp.http.Cache-Control ~ "private") { 215 | set beresp.http.X-Cacheable = "NO:Cache-Control=private"; 216 | } 217 | } 218 | 219 | sub vcl_deliver { 220 | # oh noes backend is down 221 | if (resp.status == 503) { 222 | return(restart); 223 | } 224 | 225 | # Debug header 226 | if(req.http.X-Cacheable) { 227 | set resp.http.X-Cacheable = req.http.X-Cacheable; 228 | } elseif(obj.uncacheable) { 229 | if(!resp.http.X-Cacheable) { 230 | set resp.http.X-Cacheable = "NO:UNCACHEABLE"; 231 | } 232 | } elseif(!resp.http.X-Cacheable) { 233 | set resp.http.X-Cacheable = "YES"; 234 | } 235 | 236 | set resp.http.x-cache-status = req.http.x-cache-status; 237 | set resp.http.x-varnish = resp.http.x-varnish + " " + req.http.x-cache-status; 238 | 239 | # Cleanup of headers 240 | unset resp.http.x-url; 241 | unset resp.http.x-host; 242 | } 243 | -------------------------------------------------------------------------------- /webserver/nginx.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | worker_processes auto; 3 | 4 | error_log /var/log/nginx/error.log warn; 5 | pid /var/run/nginx.pid; 6 | 7 | 8 | events { 9 | worker_connections 1024; 10 | } 11 | 12 | 13 | http { 14 | include /etc/nginx/mime.types; 15 | default_type application/octet-stream; 16 | 17 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 18 | '$status $body_bytes_sent "$http_referer" ' 19 | '"$http_user_agent" "$http_x_forwarded_for"'; 20 | 21 | access_log /var/log/nginx/access.log main; 22 | 23 | sendfile on; 24 | #tcp_nopush on; 25 | 26 | keepalive_timeout 65; 27 | 28 | #gzip on; 29 | 30 | include /etc/nginx/conf.d/*.conf; 31 | } -------------------------------------------------------------------------------- /webserver/ssl-option/options-ssl-nginx.conf: -------------------------------------------------------------------------------- 1 | ssl_session_cache shared:le_nginx_SSL:1m; 2 | ssl_session_timeout 1440m; 3 | 4 | ssl_protocols TLSv1.2 TLSv1.3; 5 | ssl_prefer_server_ciphers on; 6 | ssl_ciphers "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS"; 7 | 8 | # add_header Strict-Transport-Security "max-age=31536000" always; 9 | # add_header Content-Security-Policy upgrade-insecure-requests; 10 | ssl_stapling on; 11 | ssl_stapling_verify on; 12 | resolver 127.0.0.11; 13 | -------------------------------------------------------------------------------- /webserver/templates/nginx.conf.template: -------------------------------------------------------------------------------- 1 | upstream docker_wordpress { 2 | server wordpress:9000; 3 | } 4 | 5 | upstream docker_varnish { 6 | server varnish:8080; 7 | } 8 | 9 | map $scheme $proxy_x_forwarded_ssl { 10 | default off; 11 | https on; 12 | } 13 | 14 | server { 15 | listen ${NGINX_PORT} default_server; 16 | listen [::]:${NGINX_PORT} ipv6only=on default_server; 17 | # 18 | server_name ${NGINX_HOST} www.${NGINX_HOST}; 19 | # 20 | location ~ ^/.well-known/acme-challenge/ { 21 | root /tmp/acme-challenge; 22 | } 23 | # 24 | #location / { 25 | #port_in_redirect off; 26 | #return 301 https://$host$request_uri; 27 | #} 28 | } 29 | 30 | server { 31 | #listen 443 ssl; 32 | #listen [::]:443 ipv6only=on ssl; 33 | # 34 | server_name ${NGINX_HOST} www.${NGINX_HOST}; 35 | # 36 | #ssl_certificate /etc/letsencrypt/live/${NGINX_HOST}/fullchain.pem; 37 | #ssl_certificate_key /etc/letsencrypt/live/${NGINX_HOST}/privkey.pem; 38 | #ssl_trusted_certificate /etc/letsencrypt/live/${NGINX_HOST}/chain.pem; 39 | #ssl_dhparam /etc/letsencrypt/ssl-dhparam.pem; 40 | include /etc/letsencrypt/options-ssl-nginx.conf; 41 | # 42 | client_max_body_size 8m; 43 | proxy_headers_hash_max_size 768; 44 | proxy_headers_hash_bucket_size 128; 45 | # 46 | 47 | location / { 48 | proxy_pass http://docker_varnish; 49 | proxy_set_header X-Real-IP $remote_addr; 50 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 51 | proxy_set_header X-Forwarded-Proto $scheme; 52 | proxy_set_header Host $host; 53 | proxy_set_header X-Forwarded-Host $host; 54 | proxy_set_header X-Forwarded-Port $server_port; 55 | proxy_set_header Upgrade $http_upgrade; 56 | proxy_set_header Connection "Upgrade"; 57 | proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl; 58 | 59 | # 60 | proxy_redirect off; 61 | proxy_buffering on; 62 | proxy_buffer_size 128k; 63 | proxy_buffers 100 8k; 64 | proxy_connect_timeout 90; 65 | proxy_send_timeout 90; 66 | proxy_read_timeout 90; 67 | proxy_request_buffering off; 68 | # 69 | # Mitigate httpoxy attack 70 | proxy_set_header Proxy ""; 71 | } 72 | # 73 | access_log off; 74 | error_log /var/log/nginx/${NGINX_HOST}-443.error.log error; 75 | } 76 | 77 | server { 78 | #listen 90; 79 | # 80 | server_name ${NGINX_HOST} www.${NGINX_HOST}; 81 | # 82 | 83 | root /var/www/html; 84 | index index.php; 85 | 86 | location / { 87 | try_files $uri $uri/ /index.php$is_args$args; 88 | } 89 | 90 | location ~ \.php$ { 91 | #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini 92 | include fastcgi_params; 93 | fastcgi_intercept_errors on; 94 | fastcgi_pass docker_wordpress; 95 | #The following parameter can be also included in fastcgi_params file 96 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 97 | } 98 | # 99 | # 100 | # deny access to .htaccess vb. files, if Apache's document root 101 | location ~/\. { 102 | deny all; 103 | log_not_found off; 104 | } 105 | # 106 | location = /favicon.ico { 107 | log_not_found off; 108 | access_log off; 109 | } 110 | # 111 | location = /robots.txt { 112 | allow all; 113 | log_not_found off; 114 | access_log off; 115 | } 116 | # 117 | access_log off; 118 | error_log /var/log/nginx/${NGINX_HOST}-90.error.log error; 119 | } 120 | -------------------------------------------------------------------------------- /wordpress/README.md: -------------------------------------------------------------------------------- 1 | # WordPress 2 | 3 | add and/or remove wordpress themes, plugins or custom code folders and files with any ftp client program to ./wordpress folder 4 |

./wordpress/wp-config.php file is located in the root of your WordPress file directory and contains your website’s base configuration details, such as database connection information. 5 | You can set custom configuration for your website in this file. --------------------------------------------------------------------------------