Documentation for the WordPress site hosted at enclari.com.
wp-net (internal) + web (external)/opt/stack/wordpress/docker-compose.ymldb-data)./html → /var/www/htmlwebsecure with Let's Encrypt DNS-01 Hetznernetworks:
web:
external: true
wp-net:
volumes:
db-data:
services:
wp-db:
image: mariadb:10.11
restart: unless-stopped
environment:
MARIADB_DATABASE: ${WP_DB_NAME}
MARIADB_USER: ${WP_DB_USER}
MARIADB_PASSWORD: ${WP_DB_PASSWORD}
MARIADB_ROOT_PASSWORD: ${WP_DB_ROOT_PASSWORD}
volumes:
- db-data:/var/lib/mysql
networks:
wp-net:
aliases: ["wp-db"]
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-u", "root", "-p${WP_DB_ROOT_PASSWORD}"]
interval: 5s
timeout: 3s
retries: 20
wordpress:
image: wordpress:6-apache
user: "33:33"
cap_drop: ["ALL"]
restart: unless-stopped
depends_on:
wp-db:
condition: service_healthy
environment:
WORDPRESS_DB_HOST: wp-db:3306
WORDPRESS_DB_USER: ${WP_DB_USER}
WORDPRESS_DB_PASSWORD: ${WP_DB_PASSWORD}
WORDPRESS_DB_NAME: ${WP_DB_NAME}
WORDPRESS_DB_CHARSET: utf8mb4
WORDPRESS_TABLE_PREFIX: ${WP_TABLE_PREFIX}
WORDPRESS_CONFIG_EXTRA: |
define('WP_HOME','https://${DOMAIN_APEX}');
define('WP_SITEURL','https://${DOMAIN_APEX}');
if (isset($$_SERVER['HTTP_X_FORWARDED_PROTO']) && $$_SERVER['HTTP_X_FORWARDED_PROTO']==='https'){
$$_SERVER['HTTPS']='on';
}
define('FORCE_SSL_ADMIN', true);
volumes:
- ./html:/var/www/html
networks: ["wp-net", "web"]
labels:
traefik.enable: "true"
traefik.docker.network: web
traefik.http.routers.wp.rule: "Host(`${DOMAIN_APEX}`)"
traefik.http.routers.wp.entrypoints: websecure
traefik.http.routers.wp.tls: "true"
traefik.http.routers.wp.tls.certresolver: le
traefik.http.routers.wp.service: wp
traefik.http.services.wp.loadbalancer.server.port: "80"
traefik.http.routers.wp-www.rule: "Host(`www.${DOMAIN_APEX}`)"
traefik.http.routers.wp-www.entrypoints: websecure
traefik.http.routers.wp-www.tls: "true"
traefik.http.routers.wp-www.middlewares: to-apex
traefik.http.middlewares.to-apex.redirectregex.regex: "^https://www\\.${DOMAIN_APEX}/(.*)"
traefik.http.middlewares.to-apex.redirectregex.replacement: "https://${DOMAIN_APEX}/$${1}"
traefik.http.middlewares.to-apex.redirectregex.permanent: "true"
pma:
image: phpmyadmin:5-apache
restart: unless-stopped
depends_on:
wp-db:
condition: service_healthy
environment:
PMA_HOST: wp-db
PMA_USER: ${WP_DB_USER}
PMA_PASSWORD: ${WP_DB_PASSWORD}
UPLOAD_LIMIT: 256M
MEMORY_LIMIT: 256M
PMA_ABSOLUTE_URI: https://pma.${DOMAIN_APEX}/
networks: ["wp-net", "web"]
labels:
traefik.enable: "true"
traefik.docker.network: web
traefik.http.routers.pma.rule: "Host(`pma.${DOMAIN_APEX}`)"
traefik.http.routers.pma.entrypoints: websecure
traefik.http.routers.pma.tls: "true"
traefik.http.routers.pma.tls.certresolver: le
traefik.http.routers.pma.service: pma
traefik.http.services.pma.loadbalancer.server.port: "80"
traefik.http.routers.pma.middlewares: authentik-forwardauth@file,secure-headers@docker
Covered by global backup script with tag service=wp.
What to include
/opt/stack/wordpressdb-dataManual backup
/root/backup.sh backup wp
restic snapshots --tag service=wp
Restore flow
cd /opt/stack/wordpress && docker compose down
rsync -aHAX --delete /opt/restore/wp-<TS>/opt/stack/wordpress/ /opt/stack/wordpress/
for V in $(docker volume ls -q | grep '^wordpress_'); do
rsync -aHAX --delete /opt/restore/wp-<TS>/var/lib/docker/volumes/$V/_data/ /var/lib/docker/volumes/$V/_data/
done
docker compose up -d
# Update WordPress and phpMyAdmin
cd /opt/stack/wordpress
docker compose pull && docker compose up -d
# Logs
docker compose logs -f --tail=200 wordpress
docker compose logs -f --tail=200 pma
# DB shell
docker exec -it $(docker ps --filter name=wordpress-wp-db --format '{{.ID}}') mariadb -u root -p