Migrating a Wordpress local install to a Traefik Dockerised stack
I have two legacy Wordpress sites on a VPS with 2GB memory. I'd like to move them to an upgraded Ubuntu keeping Nginx, and at the same time - Dockerise!
I have two legacy Wordpress sites on a VPS with 2GB memory. I'd like to move them to an upgraded Ubuntu keeping Nginx, and at the same time - Dockerise!
My migration target is a Wordpress dual stack, with Nginx and Maria-DB which I just built, as per this post. The addition of an Nginx layer might not be necessary for your purposes, in which case you would probably substitute the Wordpress Docker image I'm using here, with a version that has Apache included.
The before picture - locally installed Wordpress with a shared MySQL instance.

The after picture - multiple Wordpress in a Traefik based Docker stack

Interim domain names
As I am building all of this while my production sites are up and running, I will need to create interim domain names and point at the new server. I will use the convention of "new" at the front, such as new.yourblog1.com.
The second stack is Wordpress Multisite, so it will be interesting to see how I can forward multiple domains in this configuration. I'll cover that migration in a subsequent post.
If you have the cahones there is the option of pointing the production domains to your new server and just being down while you hack about, but I wouldn't recommend it. Having said that, I might have a go at this for my second stack, once I have a solid config for the first.
Import and configure Nginx
There are a number of parts of the Nginx build that have important configuration that I think I will need to use.
| Nginx File | Purpose | Strategy |
|---|---|---|
| /etc/nginx/nginx.conf | Contains http block, param for xml-rpc rate limiting | Try rate limiter in the main config file |
| /etc/nginx/modules-enabled | Contains multiple | Check it works ok without, possibly enable |
| /etc/nginx/sites-enabled/sitename | Contains my server blocks. | Migrate to default.conf file, remove SSL. Split the two WP instances. |
| /etc/nginx/global/ipfilter.conf | IP filter whitelist | Keep, migrate to new .inc files |
| /etc/nginx/global/restrictions.conf | File I made with a few security tweaks | Keep, migrate to new .inc files |
| /etc/nginx/snippets/* | Various cache and SSL files | Ignore SSL parts, repoint to fastcgi-php.conf |
Nginx default modules
This is the list of modules in the default Nginx Alpine container. It doesn't match my current module list, so I am hoping there are no module changes needed.
ngx_http_geoip_module-debug.so, ngx_http_js_module-debug.so, ngx_stream_geoip_module-debug.so, ngx_http_geoip_module.so, ngx_http_js_module.so, ngx_stream_geoip_module.so, ngx_http_image_filter_module-debug.so, ngx_http_xslt_filter_module-debug.so, ngx_stream_js_module-debug.so, ngx_http_image_filter_module.so, ngx_http_xslt_filter_module.so, ngx_stream_js_module.soBreak apart the two site configs
My planned steps for Nginx setup.
- Create /data/nginx-wp1 and 2, attach the volumes
- Extract the supplied nginx.conf, add my xml-rpc rate limiter definition, add to the shared volumes, and attach the file
- Copy in the ipfilter.conf and restrictions.conf to each shared volume with .inc suffixes and edit network information
- Migrate the server blocks
Attaching the Nginx volumes
I will apply the volumes as such (full compose file at the end of the post). All of the "left side of the colon" directories will need to exist before applying. This example is the nginx-wp1 docker service:
volumes:
- /data/nginx-wp1:/etc/nginx/conf.d
- /data/nginx-wp1/nginx.conf.new:/etc/nginx/nginx.conf
- /data/logs/nginx-wp1:/var/log/nginx
- /data/wp1:/var/www/htmlThe default nginx.conf extracted from the container
There are instructions on how to extract such things on the Nginx Docker Hub page. This is the supplied nginx.conf, which I am going to mess with as little as possible.
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}You can probably ignore this part but I am adding as it's interesting should you wish to customise the http block / nginx.conf.
I inject my line limit_req_zone $binary_remote_addr zone=one:10m rate=30r/m; which defines a rate limit spec with alias "one".
It's arbitrary as long as it's a shared location, I will use my /data/nginx-wp1 & 2 volumes and map the individual file. I add an entry to my volumes for both nginx services as: - /data/nginx-global/nginx.conf.new:/etc/nginx/nginx.conf
I save the file as /data/nginx-wp1/nginx.conf.new & also in 2, and map them.
Create a PHP invocation wrapper
Every time a php script is to be read, there are a number of lines repeated, so it's more concise to make a small include script. I create a fastcgi-php.inc script with these contents, copied from the default server block, with these contents:
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;Every time I have a PHP invocation, I precede it with the include, as you will see throughout the examples:
location ~ \.php$ {
include conf.d/fastcgi-php.inc;
fastcgi_pass wp1:9000;
}Copy in my Nginx global include files
I copy my ipfilter.conf and restrictions.conf files into /data/nginx-wp1 & 2 and edit, changing the suffixes to .inc. If you are not interested in IP whitelisting or my small security tweaks, scroll forward to the server block config section.
My ipfilter.conf file is like this.
# Whitelist for login page
location ^~ /wp-login.php {
# Static IP of authorised location 1
allow 1.2.3.4/32 ;
# Coworking location
allow 2.3.4.5/32 ;
deny all;
include conf.d/fastcgi-php.inc;
fastcgi_pass wp1:9000; # wp1 is the wordpress docker network
}
location /wp-admin {
location ~ /wp-admin/admin-ajax.php$ {
include conf.d/fastcgi-php.inc;
fastcgi_pass wp1:9000; # wp1 is the wordpress docker network name
}
location ~* /wp-admin/.*\.php$ {
# Static IP of authorised location 1
allow 1.2.3.4/32 ;
# Coworking location
allow 2.3.4.5/32 ;
deny all;
include conf.d/fastcgi-php.inc;
fastcgi_pass wp1:9000; # wp1 is the wordpress docker network name
}
}Note the wp1:9000 entries. The wp1 must correspond to your wordpress docker-compose service name. These are needed for any PHP invocation. The nginx-wp2 ipfilter file will need wp2 .
I copy in my restrictions.conf as restrictions.inc and tweak for the service names.
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /xmlrpc.php {
limit_req zone=one burst=3 nodelay;
include conf.d/fastcgi-php.inc;
fastcgi_pass wp1:9000; # This is the Wordpress docker network
}
location ~* /(?:uploads|files)/.*\.php$ {
deny all;
}As with the ipfilter, I will make a copy for nginx-wp2 with update wp2:9000 in it.
Nginx server block configuration
I take my legacy server block config files and attempt to clean up, gleefully removing SSL and 80-> 443 redirects. The new file will be one per stack, so I will make it as a new default.conf, which lives in the /data/nginx-wp1 and 2 directories. Things will start to get interesting between 1 and 2 as site 2 is a multisite. For WP1 I am going with the following.
server {
listen 80 ;
listen [::]:80 ;
root /var/www/html;
index index.php index.html index.htm index.nginx-debian.html;
real_ip_header X-Forwarded-For;
set_real_ip_from traefik; # Your internal Traefik network name
server_name yourblog1.com www.yourblog1.com ;
include conf.d/restrictions.inc;
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
include conf.d/ipfilter.inc ;
location ~ \.php$ {
include conf.d/fastcgi-php.inc;
fastcgi_pass wp1:9000;
}
location ~ /\.ht {
deny all;
}
location ~* \.(css|gif|ico|jpeg|jpg|js|png|woff|ttf)$ {
expires max;
log_not_found off;
}
}As with the other files, the fastcgi_pass must be pointed at the right WP docker network target, wp1:9000.
I went through all sorts of pain finding a way to pass through the real IP address to Nginx for whitelisting purposes. I thought it needed a manual addition of the ngx_http_realip_module module but nay, it appears the real IP can be accessed with the standard alpine image, by using Nginx to grab it from Traefik, hence the two lines:
real_ip_header X-Forwarded-For;
set_real_ip_from traefik; # Your internal Traefik network nameFrom this point on, allow/deny rules etc should automatically pick up the new addresses. You will need to use the Traefik network name for your service as defined in the docker-compose services: section. In all my examples it's just "traefik".
Permissions
I have run the following at the shell in order to set web server permissions and allow my ubuntu user to edit files:
$ cd /data
$ sudo chown -R www-data:www-data nginx-wp?
$ sudo chmod 660 nginx-wp?
$ sudo usermod -aG www-data ${USER}Logging out and back in should apply the group to your user. I haven't spent a lot of time looking at permissions, this might need more tuning.
Test IP whitelist (optional)
If you are on the IP whitelist track, simply reloading the wp-login page while altering the wp-login part of the whitelist, or if already logged in, reloading the wp-admin page while altering that section of the whitelist should suffice.
Always test. You should see entries in the access log alternating between 200's and 403's. I fully tested this config while sitting at the default installation screen - it has wp-admin in the path.
Export/Import your production Wordpress database and site files
And you thought you were having fun so far! For the migration you will need two things, a dump of the database, and a copy of the Wordpress html/php files. They'll need to be at the same point in time, so you'll have to generate/copy them while there are no site updates going on.
Be aware that even after a successful import, a lot of the config will be dropped, such as theme settings and widgets, so you'll need some time to tweak all of that. If you have and API connections, licensed plugins or login credentials (e.g. SMTP) ensure you have these on hand ready to reapply, regenerate or reconnect.
Export the Database
I am making some assumptions all over the place here, such as you are capable of connecting to mysql as root, moving files between servers, etc, and won't dwell on how to do that sort of thing. This will be more of a bunch of specific steps I am following that might be of use to you.
Connect to mysql as root and run show databases;. If you have some legacy database that has been shared, as I have, there will be multiple databases. You might just have one which by default is called "wordpress". For I am doing (single DB container) I just want to extract a single DB.
If the DB was called wordpress, you would drop to the operating system prompt and run this (mysql passwd will be prompted). It's non-destructive so shouldn't hurt your running DB. If it's not called wordpress, change the wordpress name in the below command.
mysqldump -u root -p wordpress | gzip - > siteDump.wordpress.sql.gz
Move the file to your new server.
Export the Wordpress html directory tree
There's nothing too complex here, we're just rolling up the HTML files. I change directory to my HTML files and create a compressed tarball. This example assumes you have it all under /var/www/html, i.e. the path for your config file is /var/www/html/wp-config.php. You might want to have a wander through this tree and look for old junk filling it up and remove it first. It can get large with images, uploads, themes, plugins and general Wordpress bloat. And it's likely file transfers will eat into your bandwidth allowance.
From within the file tree, (the same directory as wp-config.php) we can create an archive in the directory above.
$ cd /var/www/html
$ tar -zcvf ../wordpress.tar.gz .Move this new .gz file also to your new server.
Import the old database into the new image
On your new server, we set up the DB first. We import all the data as is, we can rename the database but we don't yet rename any URLs, it will still have production ones.
If you ran through the previous post about a default installation, there will already be a database for mariadb1, so let's delete the contents. Get the container name from "docker ps". Make sure you are in the right place before running that rm -r.
$ docker stop traefik_mariadb1_1
$ cd /data/mariadb1
$ sudo rm -r *
$ cd [to wherever your compose file is]
$ docker-compose up -dThe docker compose should already be set up to recreate the whole instance with a database called wp1.
If you gzipped the SQL dump file, gunzip it. Here's my command which assumes you have a running container called traefik_mariadb1_1 (check with "docker ps").
docker exec -i traefik_mariadb1_1 mysql -uroot -psecure_password_123 wp1 < siteDump.wordpress.sql
This will import the legacy database into the wp1 database.
Alter the database URLs to "new" URLs.. or not.
Decision time. If you are going to fire this blog up with a temporary URL, we alter the name at this point. I imagine only the most unimportant blogs, where downtime doesn't matter, would be a big bang switch, otherwise we rename. This allows you at leisure to preconfigure before renaming back.
I've had a script lying around for eons, I don't even know where it's from. It will chug through the DB and rename all the URLs. Use at your own risk. I save as dbreplace.sh, remember to chmod +x it.
It is best to run this inside the container, but first let's create it in the shared volume.
CD to /data/mariadb1
vim (or favourite text editor) the new file.
sudo vim dbreplace.sh
..and paste in the below. After saving, chmod +x.
#!/bin/bash
echo -n "Enter username: " ; read db_user
echo -n "Enter $db_user password: " ; stty -echo ; read db_passwd ; stty echo ;
echo ""
echo -n "Enter database name: " ; read db_name
echo -n "Enter search string: " ; read search_string
echo -n "Enter replacement string: " ; read replacement_string
MYSQL="/usr/bin/mysql --skip-column-names -u${db_user} -p${db_passwd}"
echo "SHOW TABLES;" | $MYSQL $db_name | while read db_table
do
echo "SHOW COLUMNS FROM $db_table;" | $MYSQL $db_name| \
awk -F'\t' '{print $1}' |grep -v "default" |grep -v "values" | while read tbl_column
do
#echo "update $db_table set ${tbl_column} = replace(${tbl_column}, '${search_string}', '${replacement_string}');"
echo "update $db_table set ${tbl_column} = replace(${tbl_column} , '${search_string}', '${replacement_string}');" | $MYSQL $db_name
done
doneWe enter the container thus, giving your container name (found with docker ps).
docker exec -it traefik_mariadb1_1 bash
Again, use this script at your own risk. At the # prompt, cd to /var/lib/mysql and it should be visible - run it. The password will be your new DB root user, and the database name will be wp1. Your dialogue might look like this.
# ./dbreplace.sh
Enter username: root
Enter root password:
Enter database name: wp1
Enter search string: yourblog1.com
Enter replacement string: new.yourblog1.comIt will replace for example, yourblog1.com with new.yourblog1.com . Your DNS zone file will of course need to be able to resolve subdomains for this to work, e.g. *.yourblog1.com. When you are ready to switch over production, you can alter the blog name through the Wordpress admin panel itself but is still worth running it again in reverse, for the many scrappy strings lying about.
Your chosen name will need to match the traefik label, e.g.
- traefik.frontend.rule=Host:new.yourblog1.com,www.new.yourblog1.com
Import the Wordpress HTML directory tree
If you are following this build, we'll be unpacking wordpress.tar.gz into /data/wp1. If you set up the default WP install from the last post, it will have files already in there. remove them all first - before you move the tarball in there.
$ docker stop traefik_wp1_1 traefik_nginx-wp1_1
$ cd /data/wp1Check the tarball is not here. In /data/wp1 because I am replacing an old build I clear all files:
$ sudo rm -r * .htaccessYou might not have a .htaccess - ignore errors removing. Copy/move the tarball into /data/wp1.
$ tar xvf wordpress.tar.gz
$ cd [to wherever your compose file is]
$ docker-compose up -dThis should unpack everything relative to your current directory. When done, you should see /data/wp1/wp-config.php, and everything else. As if by magic, after the compose run you should see a version of your old site on the new url. If you get redirected back to the original URL, try a private window, your browser session has probably been mashed.
Be aware - Wordpress re-config will be necessary
It probably looks different, right? A lot of config has been dropped and you will need to repair. Luckily, we have the production system available to to a side-by side reconfiguration. It depends on your site size and complexity, but it should be less that an hours work if you have access to all the external info you need.
A loose guide to reconfiguring the Wordpress clone
I suggest making notes as you go for all of this. Even after fixing up the new site, when I switched over, it dumped some settings again and I had to reapply, without the benefit of the production site to refer to. (Menu, widgets, backgorund)
The #1 thing to do with your new clone site is to go in to the Wordpress admin panel and disable crawling by checking the box at Settings -> Reading -> Search Engine Visibility -> Discourage search engines from indexing this site and saving.
It's also the first thing you'll need to uncheck at go-live time if you are switching over your production domain to this server.
Update Apperance -> Customize
There is no easy way to do this. Bring up the production site in a second screen, and go through every field until you have set the new site up the same.
You might find that if you have a third party theme they have their own config pages you will also need to update. e.g. Elegant themes Divi -> Theme Options. Sometimes this is where the site logo is assigned, for example.
Update Appearance -> Menus
If it hasn't already been done in the theme customiser, you will probably need to reattach the menu, assigning in the Display location.
Update Appearance -> Widgets
In my case the theme makers have their own bloatware widget system that I have to mess with.
Note - if you did have a bunch of widgets in a screen area that have been dropped, you might find them still there but in an "Inactive Sidebar" group which just needs to be replaced.
The external service juxtaposition
My "new" site had a polite message popping up telling me my SMTP plugin needed reconfiguring. Looking at the production site, it turns out that it's set up from back when I had a different email host. That account is long gone, so email sending would have been broken. Oh how I laughed. Anyway it's a good example of the sort of thing you will need to reconfigure.
But what about APIs? You probably have connections out to things like Google Analytics, Akismet, Wordfence, Jetpack, social media repeaters. I would say rather than reconfigure all of these to reconnect with/from hour new domain, perhaps leave this and be ready to go through them all immediately after switching over your new domain.
It's not a good idea having a clone site reporting to say, Google webmaster or analytics, APIs or social media app links, with a new interim name, at the same time as your production, for any period of time, so after you fire up your clone site best not dally for a long time.
Performance test of the clone site
Before I flip the switch I will do a quick comparison of the performance on a sample page using webpagetest.org. My original is in Europe and my new is on the US West coast, so I will try and get test servers geographically close.
- Current production, on the middle run is returning 5.5s to visually complete with 1475KB bytes.
- The new site is returning 5.7s to visually complete, but curiously with 2644KB bytes.
A quick skim of the larger files downloaded show the theme provider's CSS payload is 700KB, compared to 90KB on the old server. I can investigate this later but otherwise am good to go.
I remind myself that I have done nothing with the default Nginx configs either, so it's off the shelf settings there. I suspect gzip is not running, for example.
Go live with my new site
Time to press the big red button. I am going to have to do two things in fairly close succession. Do a database rename of the site, and switch over the DNS. Well actually three. Wordpress admin rename, database script rename, then DNS switch.
Go to Settings -> General. These are the two fields (Wordpress Address, Site Address) you will change to the production URL. Once you submit at this point the admin panel won't be accessible any longer - traffic will go back to the old production - until you finish the rest. I change the URLs.
Next, I repeat the database search and replace again, this time changing the new name to the production name.
Lastly, I change my DNS root domain and wildcard subdomain to point to the new address.
I edit the docker-compose.yml and change the Traefik Nginx service label from the temporary URL back to the production URL, and update the docker stack.
It can take some time for the DNS change to propagate. Monitor, and as soon as you get into the new production site, re-enable crawling.
Tragically I found that some of the settings were dropped again, notably widgets, menu assignment and background image. Luckily I could get these fixed within 5 minutes.
Cleanup after going live
I jump immediately to the Wordfence plugin pages to check my firewalls are up. They aren't - permission errors. I then realise that as PHP and Wordpress are not running as my www-data user, number 33. I return to the docker-compose.yml file and make the magic happen. I'll ripple back and update the previous post about that.
I check cache, minify plugins, search plugins, purge and reset caches on all of those. The extent of your testing is up to you relative to the importance of the site - you might want to step through every single plugin.
I won't be shutting down the old server just yet, as I still have "WP2" to migrate. Right now I am going to do a bit of side by side comparison of the Nginx settings to see if I can solve the mystery of the additional 1MB. From my old nginx.conf, I find the following gzip settings. They would have been there for a long time, and I can't even remember why they are what they are, and might not be the most efficient, but I'll start by porting these by just pasting into my new nginx.conf at /data/nginx-wp1/nginx.conf.new. It has a line #gzip on; so underneath that seems to make sense.
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 2;
gzip_buffers 16 8k;
gzip_min_length 1100;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;I restart my nginx-wp1 container. Viola - the download payload has dropped back to 1.6MB, with the theme CSS down to 90KB again. My visually complete time is 6.8 seconds. I think, this is because Google Ads has actually started working. It seems my article is there in 3s but the ad bloat increases it. I am thinking of dumping Ads altogether on that site, it earns me virtually nothing anyway, in the hope that I might find a site sponsor instead, but that's another story.
If a little while later you restart and find that all traffic is dumped to the install URL, it may be that either the wp_ prefix is custom and needs to be added to the environment vars, or that caching is enabled. I had this problem and setting false the parameter: define(‘WP_CACHE’, false); in wp-config.php saved the day. If you find problems post-install cache and minify functions are a good place to start.
I would also recommend getting your logging sorted out - I have a post on that.
The end - and the complete files as a reference.
I am sure even the crawlers have fallen asleep by now. More to come with my multisite migration post, but here's my compose file and nginx.conf.new file.
Traefik.toml is based on this Digital Ocean build tutorial.
Example docker-compose.yml - Traefik, Nginx, Wordpress, MariaDB
version: "3"
networks:
web:
external: true
internal:
external: false
driver: bridge
services:
traefik:
image: traefik:maroilles-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /data/traefik/traefik.toml:/traefik.toml
- /data/traefik/acme.json:/acme.json
labels:
- traefik.backend=traefik
- traefik.frontend.rule=Host:monitor.yourhost.com
- traefik.port=8080
networks:
- web
nginx-wp1:
image: nginx:alpine
restart: unless-stopped
ports:
- '80'
volumes:
- /data/nginx-wp1:/etc/nginx/conf.d
- /data/nginx-wp1/nginx.conf.new:/etc/nginx/nginx.conf
- /data/logs/nginx-wp1:/var/log/nginx
- /data/wp1:/var/www/html
labels:
- traefik.backend=nginxwp1
- traefik.frontend.rule=Host:yourblog1.com,www.yourblog1.com
- traefik.docker.network=web
networks:
- web
- internal
depends_on:
- wp1
nginx-wp2:
image: nginx:alpine
restart: unless-stopped
ports:
- '80'
volumes:
- /data/nginx-wp2:/etc/nginx/conf.d
- /data/nginx-wp2/nginx.conf.new:/etc/nginx/nginx.conf
- /data/logs/nginx-wp2:/var/log/nginx
- /data/wp2:/var/www/html
labels:
- traefik.backend=nginxwp2
- traefik.frontend.rule=Host:yourblog2.com,www.yourblog2.com
- traefik.docker.network=web
networks:
- web
- internal
depends_on:
- wp2
mariadb1: # This DB service name is also the DB Host name
image: mariadb:10.7
command: --max-allowed-packet=128MB
restart: unless-stopped
volumes:
- /data/mariadb1:/var/lib/mysql
labels:
- traefik.enable=false
environment:
MYSQL_ROOT_PASSWORD:
MYSQL_DATABASE: wp1 # This name is internal to the DB
MYSQL_USER: wp1_user
MYSQL_PASSWORD: secure_password_123
networks:
- internal
mariadb2: # This DB service name is also the DB Host name
image: mariadb:10.7
command: --max-allowed-packet=128MB
restart: unless-stopped
volumes:
- /data/mariadb2:/var/lib/mysql
labels:
- traefik.enable=false
environment:
MYSQL_ROOT_PASSWORD:
MYSQL_DATABASE: wp2 # This name is internal to the DB
MYSQL_USER: wp2_user
MYSQL_PASSWORD: secure_password_123
networks:
- internal
wp1: # This is called from the Nginx config file
image: wordpress:php7.3-fpm-alpine
restart: unless-stopped
user: "33"
environment:
WORDPRESS_DB_HOST: mariadb1 # This must match the DB service name
WORDPRESS_DB_NAME: wp1
WORDPRESS_DB_USER: wp1_user
WORDPRESS_DB_PASSWORD: secure_password_123
labels:
- traefik.enable=false
networks:
- internal
volumes:
- /data/wp1:/var/www/html
depends_on:
- mariadb1
wp2: # This is called from the Nginx config file
image: wordpress:php7.3-fpm-alpine
restart: unless-stopped
user: "33"
environment:
WORDPRESS_DB_HOST: mariadb2 # This must match the DB service name
WORDPRESS_DB_NAME: wp2
WORDPRESS_DB_USER: wp2_user
WORDPRESS_DB_PASSWORD: secure_password_123
labels:
- traefik.enable=false
networks:
- internal
volumes:
- /data/wp2:/var/www/html
depends_on:
- mariadb2Example nginx.conf - mounted from nginx.conf.new
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 2;
gzip_buffers 16 8k;
gzip_min_length 1100;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
limit_req_zone $binary_remote_addr zone=one:10m rate=30r/m;
include /etc/nginx/conf.d/*.conf;
}Main photo courtesy of Ray Hennessy on Unsplash.
Retrospective blog post to use BlueSky for comments: techroads.org/migrating-a-... #Wordpress #Docker
— TechRoads blog (@techroads.org) Feb 22, 2024 at 11:35 am
[image or embed]