I have two containers, one for mariadb and the other for wordpress.
I'm using this configuration for mariadb:
version: "3"
services:
mariadb:
image: mariadb
container_name: mariadb
environment:
MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD}"
volumes:
- ./data:/var/lib/mysql
ports:
- "127.0.0.1:3306:3306"
restart: unless-stopped
networks:
- web
networks:
web:
external: true
And this for wordpress
version: '3'
services:
# Wordpress
myWordpress:
image: wordpress:php8.1
container_name: myWordpress
hostname: myWordpress
restart: unless-stopped
volumes: [ './data:/var/www/html' ]
environment:
- WORDPRESS_DB_HOST=mariadb
- WORDPRESS_DB_USER="${WORDPRESS_DB_USER}"
- WORDPRESS_DB_PASSWORD="${WORDPRESS_DB_PASSWORD}"
- WORDPRESS_DB_NAME="${WORDPRESS_DB_NAME}"
networks:
- web
networks:
web:
external: true
I already created the network web and logged in mysql root , created the database & user and executed this command: GRANT ALL PRIVILEGES ON wp_db.* TO 'wp_user'#'%';.
Then I logged in mysql using that user and its password.
docker exec -it mariadb bash
mysql -u wp_user -pMyPassword
# Welcome to the MariaDB monitor. Commands end with ; or \g.
# ...
The issue appears when I visit wordpress, I see this message:
Warning: mysqli_real_connect(): (HY000/1045): Access denied for user '"wp_user"'#'172.16.32.5' (using password: YES) in /var/www/html/wp-includes/wp-db.php on line 1753
Access denied for user '"wp_user"'#'172.16.32.5' (using password: YES)
I feel that the problem is in WORDPRESS_DB_HOST variable, I pointed it to mariadb container name, I also tried pointing it to localhost since both containers share the same network web, but his didn't work either, and the same exact error message appeared.
One month later, I went back to this issue and tried putting environment variables in quotes, it worked as expected.
So instead of:
- WORDPRESS_DB_USER="${WORDPRESS_DB_USER}"
It should be
- "WORDPRESS_DB_USER=${WORDPRESS_DB_USER}"
Related
Firstly, I'd like to say that I'm not a server admin. I'm a web programmer tasked with setting up a development server and I have no idea what I'm doing. I may not be doing things according to best practice or the way you might do them. Unfortunately, with Traefik, there are 3 ways to do everything and so 2/3 of the answers that I've come across aren't compatible with my implementation and I can't figure out how to make them work. Furthermore, this isn't my only (or even primary) job duty.
Here's the setup:
Single-server docker environment on a Linode server with Ubuntu 20.04
I have one stack with Traefik, Traefik Hub, Portainer, and WhoAmI configured and working (mostly) correctly. I don't have the DNS challenge working right with Let's Encrypt, but I don't really care about that at this point. I don't really need a wildcard certificate.
I created a mariadb container. We're mostly a WordPress shop and I'd like to have one container for all the databases we work with rather than configuring a database on an environment-by-environment basis.
I created an external bridge network, named "maverick-net" and all of the stacks are connected to it.
I have a self-hosted GitHub runner listening for changes to the "dev" branch of the project. The runner pulls down the latest repo, writes GitHub secrets to a local .env file, runs composer install and then docker-compose up -d. (That's the reason behind the obscenely-long bind mount paths.)
I'm trying to make the code for these WordPress projects reusable as much as possible, so there's a lot of .env variables in the different files. At some point I'll probably move those over to docker secrets, but at this point it's a development server and not as critical.
My issue is that I haven't been able to get a WordPress site up and running, and I keep hitting a "Bad Gateway" error. When I curl the URL from inside the traefik container, I get... wait for it... "Bad Gateway."
Clearly there's something I'm missing, but I've been slamming my head against a brick wall for weeks trying different approaches to get this running and I need help. There has to be something I'm not getting about docker networks in general because my wp-cli container never has been able to connect to the database, regardless of whether I start it in the same stack or if i try to connect to it on the maverick-net network.
My traefik stack (side note, I'd really like to split these command entries into static and dynamic config files, but that's a task for another day):
version: "3.9"
secrets:
linode_token:
file: "../secrets/linode_token.secret"
services:
traefik:
container_name: traefik
image: "traefik:latest"
command:
- --log.level=DEBUG
- --log.filePath=./traefik.log
- --accessLog=true
- --accessLog.filePath=./access.log
- --accessLog.bufferingSize=100
- --accessLog.filters.statusCodes=400-499
- --api
- --api.dashboard=true
- --api.insecure=false
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --providers.docker
- --providers.docker.watch=true
- --providers.docker.exposedbydefault=false
- --certificatesresolvers.leresolver.acme.dnsChallenge=true
- --certificatesresolvers.leresolver.acme.dnsChallenge.provider=linodev4
- --certificatesresolvers.leresolver.acme.httpchallenge=true
- --certificatesresolvers.leresolver.acme.httpchallenge.entrypoint=web
- --certificatesresolvers.leresolver.acme.email=xxxxxxxxxxx#xxxxxxxxx.xxx
- --certificatesresolvers.leresolver.acme.storage=./acme.json
#- --certificatesresolvers.leresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
- --certificatesresolvers.leresolver.acme.caserver=https://acme-v02.api.letsencrypt.org/directory
- --experimental.hub=true
- --hub.tls.insecure=true
- --metrics.prometheus.addrouterslabels=true
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ~/certs-data/acme.json:/data/letsencrypt/acme.json
- ./static.yml:/static.yml:ro
- ./configs:/configs
- ~/certs-data/:/data/letsencrypt/
secrets:
- "linode_token"
environment:
TZ: America/Chicago
LINODE_TOKEN_FILE: "/run/secrets/linode_token"
labels:
- "traefik.enable=true"
- "traefik.docker.network=maverick-net"
- "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
- "traefik.http.routers.http-catchall.entrypoints=web"
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
- "traefik.http.routers.traefik.tls.certresolver=leresolver"
- "traefik.http.routers.traefik.rule=Host(`XXXXX.XXXXXXXXXX.XXX`)"
- "traefik.http.routers.traefik.entrypoints=websecure"
- "traefik.http.routers.traefik.service=api#internal"
- "traefik.http.routers.traefik.middlewares=traefik-auth"
- "traefik.http.middlewares.traefik-auth.basicauth.users=XXXX:$$apr1$$XXXXX$$XXXXXXXXXXXXXXX"
- "traefik.http.routers.api.entrypoints=websecure"
networks:
- maverick-net
hub-agent:
image: ghcr.io/traefik/hub-agent-traefik:experimental
pull_policy: always
container_name: hub-agent
restart: on-failure
command:
- run
- --hub.token=XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX
- --auth-server.advertise-url=http://hub-agent
- --traefik.host=traefik
- --traefik.tls.insecure=true
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
depends_on:
- traefik
networks:
- maverick-net
portainer:
image: portainer/portainer-ce:latest
command: -H unix:///var/run/docker.sock
container_name: portainer
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
labels:
# Frontend
- "traefik.enable=true"
- "traefik.http.routers.frontend.rule=Host(`XXXXX.XXXXXXXXXX.XXX`)"
- "traefik.http.routers.frontend.entrypoints=websecure"
- "traefik.http.services.frontend.loadbalancer.server.port=9000"
- "traefik.http.routers.frontend.service=frontend"
- "traefik.http.routers.frontend.tls.certresolver=leresolver"
networks:
- maverick-net
whoami:
image: "traefik/whoami"
container_name: "whoami"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`XXXXX.XXXXXXXXXX.XXX`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.tls.certresolver=leresolver"
networks:
- maverick-net
volumes:
portainer_data:
networks:
maverick-net:
external: true
My mariadb stack:
version: "3"
networks:
# enable connection with Traefik
maverick-net:
external: true
services:
mariadb:
container_name: mariadb
image: mariadb:10.7
restart: always
volumes:
- "/home/xxxxxxxxxx/docker/mariadb/data:/var/lib/mysql"
expose:
- "3306"
env_file: .env
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PWD}
MYSQL_USER: ${ADMIN_DB_USER}
MYSQL_PASSWORD: ${ADMIN_DB_PWD}
networks:
- maverick-net
And finally my WordPress stack:
version: '3.8'
networks:
maverick-net:
external: true
# volumes:
# db_data:
services:
# mariadb:
# container_name: ${WORDPRESS_DB_NAME}-db
# image: mariadb:10.7
# restart: always
# volumes:
# - "db_data:/var/lib/mysql"
# env_file: .env
# environment:
# MYSQL_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
# MYSQL_USER: ${ADMIN_DB_USER}
# MYSQL_PASSWORD: ${ADMIN_DB_PWD}
wordpress:
container_name: ${WORDPRESS_DB_NAME}-wp
image: wordpress:6.0.2-fpm
volumes:
- type: bind
source: ${PROJECT_ROOT}/${WORDPRESS_DB_NAME}/${PROJECT_NAME}/${PROJECT_NAME}/wp
target: /var/www/html
restart: always
env_file: .env
environment:
WORDPRESS_DB_HOST: mariadb
MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
WORDPRESS_DATABASE_USER: ${WORDPRESS_DB_USER}
WORDPRESS_DATABASE_PASSWORD: ${WORDPRESS_DB_PASSWORD}
WORDPRESS_DATABASE_NAME: ${WORDPRESS_DB_NAME}
labels:
# The labels are useful for Traefik only
- "traefik.enable=true"
- "traefik.docker.network=maverick-net"
# Get the routes from https
- "traefik.http.routers.${WORDPRESS_DB_NAME}.rule=Host(`${DEV_URL}`)"
- "traefik.http.routers.${WORDPRESS_DB_NAME}.entrypoints=websecure"
- "traefik.http.routers.${WORDPRESS_DB_NAME}.tls.certresolver=leresolver"
networks:
- maverick-net
wordpress-cli:
container_name: ${WORDPRESS_DB_NAME}-cli
image: wordpress:cli
volumes:
- type: bind
source: ${PROJECT_ROOT}/${WORDPRESS_DB_NAME}/${PROJECT_NAME}/${PROJECT_NAME}/wp
target: /var/www/html
env_file: .env
environment:
WORDPRESS_DB_HOST: mariadb
MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
WORDPRESS_DATABASE_USER: ${WORDPRESS_DB_USER}
WORDPRESS_DATABASE_PASSWORD: ${WORDPRESS_DB_PASSWORD}
WORDPRESS_DATABASE_NAME: ${WORDPRESS_DB_NAME}
networks:
- maverick-net
depends_on:
- wordpress
As far as I know, you can connect to containers in the same network by using their service name.
So for example you are trying to curl to the Wordpress container from the Traefik Container.
curl 'http://wordpress/'
Should work.
In another project I use an nginx container with php-fpm.
I need to send my curl requests to the nginx container, because the php-fpm container does not handle server requests directly:
// does not work
curl 'http://php-debug/index.html'
// result
curl: (7) Failed to connect to php-debug port 80: Connection refused
// https also does not work
curl 'https://php-fpm/index.html'
// result
curl: (7) Failed to connect to php-fpm port 443: Connection refused
// This does work
curl 'http://nginx/index.html'
// result
<HTML...
For some reason https: curl requests fail, but on http I get the correct result so for local development I think it's ok.
If you are interested in a more managed solution, you can check out warden.dev. It contains a template for Wordpress too (which I use succesfully for local development). I have been using this exclusively. If you have questions how to setup WP CLI on this solution, feel free to contact me.
It comes with portainer, traefik, ssl and dns and mailhog.
Configuration is pretty straight forward, I can set up a new project within an hour and connect to the database and containers in my IDE.
https://docs.warden.dev/environments/types.html#wordpress
Here is my docker-compose.yml file:
version: '3.9'
services:
db:
image: mysql:5.7
restart: always
command: [
'--disable-partition-engine-check',
'--default_authentication_plugin=mysql_native_password',
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_unicode_ci',
'--max_allowed_packet=100M'
]
volumes:
- ./db_data:/var/lib/mysql
environment:
MYSQL_DATABASE: wordpress
MYSQL_ROOT_PASSWORD: pswd4!
pma:
image: phpmyadmin/phpmyadmin
environment:
PMA_HOST: db
PMA_PORT: 3306
MYSQL_ROOT_PASSWORD: pswd4!
UPLOAD_LIMIT: 1G
ports:
- 8080:80
depends_on:
- db
wp:
image: wordpress:php8.0
ports:
- 80:80
volumes:
- ./config/php.conf.ini:/usr/local/etc/php/conf.d/conf.ini
- ./:/var/www/html
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: root
WORDPRESS_DB_PASSWORD: pswd4!
WORDPRESS_DEBUG: 1
depends_on:
- db
links:
- db:mysql
hostname: test.localhost
wpcli:
image: wordpress:cli-php8.0
volumes_from:
- wp
depends_on:
- db
- wp
links:
- db:mysql
entrypoint: wp
command: "--info"
volumes:
db_data:
When I try to use wp-cli in Docker (e.g. docker-compose run --rm wpcli plugin list), it gets an error that it cannot connect to the database:
Error: `Access denied for user 'username_here'#'192.168.32.5' (using password: YES)`
Error establishing a database connection
This either means that the username and password information in your `wp-config.php` file is incorrect or we can’t contact the database server at `mysql`. This could mean your host’s database server is down.
Are you sure you have the correct username and password?
Are you sure you have typed the correct hostname?
Are you sure the database server is running?
If you’re unsure what these terms mean you should probably contact your host. If you still need help you can always visit the WordPress Support Forums. `Access denied for user 'username_here'#'192.168.32.5' (using password: YES)
`
It looks like wp-cli is seeing bad database credentials (username_here instead of root)
Result of executing docker-compose run --rm wpcli config list command:
What could be wrong? I've searched all over the internet, lost several hours and still haven't found the cause of the problem.
You should specify the same set of environment variables as in your wp container for your wpcli container. If not, default variables from a php file in wordpress are used.
Do be careful : volumes_from and link options are deprecated in compose v3. For the link option, docker-compose creates a network automatically (or you can specify one if you prefer) and the embed docker dns automatically attributes aliases to your containers based on their names (or in compose the service name). More info on that here
For volumes, you can find more info here
I have been working in this bitnami-wordpress-docker and still stuck on configuration. I have seen few tutorials in which the command docker-compose up -d create bunch of files inside the folder and later localhost:8000 lands to the admin part of the wordpress. But things are not being simple for me.
Here is my docker-compose.yml file.
version: '3'
services:
mariadb:
user: root
image: 'bitnami/mariadb:10.3'
volumes:
- 'mariadb_data:/bitnami'
restart: always
environment:
- MARIADB_USER=bn_wordpress
- MARIADB_DATABASE=bitnami_wordpress
- ALLOW_EMPTY_PASSWORD=yes
networks:
- wpsite
wordpress:
image: 'bitnami/wordpress:latest'
ports:
- '8000:80'
restart: always
volumes:
- 'wordpress_data:/bitnami'
depends_on:
- mariadb
environment:
- MARIADB_HOST=mariadb
- MARIADB_PORT_NUMBER=3306
- WORDPRESS_DATABASE_USER=bn_wordpress
- WORDPRESS_DATABASE_NAME=bitnami_wordpress
- ALLOW_EMPTY_PASSWORD=yes
networks:
- wpsite
phpmyadmin:
depends_on:
- mariadb
image: phpmyadmin/phpmyadmin
restart: always
ports:
- '8080:80'
environment:
PMA_HOST: mariadb
MYSQL_ROOT_PASSWORD: password
networks:
- wpsite
networks:
wpsite:
volumes:
mariadb_data:
driver: local
wordpress_data:
driver: local
The command I mentioned above is creating containers.
When I try localhost:8000, noting shows. But when localhost:8080 lands me to phpmyadmin page.
Can anybody please tell me how can I setup this Bitnami wordpress using docker. It starts getting frustrating for me.
Thank You.
Under windows, it often happens to me that some ports are occupied;)
I do that:
I start powershell or cmd as admin
running netstat -aon | findstr 8080 the last number is the port
I get TCP 0.0.0.0:8080 0.0.0.0.0 LIETENIN 3428
run taskkill /f /pid 3428
And I'm also using the vscode plugin vscode-docker to use for removing all contaners and images.
Take a look at my example of docker+wordpress+xdebug maybe something will come in handy ;)
I've a question how exactly docker-compose handles environment variables.
services:
wp:
image: wordpress:latest
container_name: "wp"
restart: unless-stopped
links:
- wpdb
environment:
- TZ=Europe/Berlin
- WORDPRESS_DB_HOST=wpdb:3306
- WORDPRESS_DB_USER=wordpress
- WORDPRESS_DB_PASSWORD=password
- WORDPRESS_DB_NAME=wp
volumes:
- ./data:/var/www/html
labels:
- "traefik.enable=true"
- "traefik.backend=wp"
- "traefik.frontend.rule=Host:MASKED"
- "traefik.port=80"
- "traefik.docker.network=web"
networks:
- internal
- web
wpdb:
image: mariadb:latest
restart: unless-stopped
container_name: "wpdb"
environment:
- MYSQL_ROOT_PASSWORD=1234
- MYSQL_USER=wordpress
- MYSQL_PASSWORD=password
- MYSQL_DATABASE=wp
networks:
- internal
labels:
- "traefik.enable=false"
volumes:
- ./sql:/var/lib/mysql
volumes:
data:
sql:
networks:
web:
external: true
internal:
The compose file works great. The containers will be created and work perfectly.
But when I change the defaults at: WORDPRESS_DB_PASSWORD=password and MYSQL_PASSWORD=password.
The Wordpress container throws access denied for user. I also tried to kill the container and volumes.
Hopefully someone has a hint for me.
You should be doing a docker-compose down -v which would delete the named volumes declared in the volumes section. The only downside is that you would be losing all the data created by the service for the first time.
Here is how I could reproduce it -
Used your compose file as reference and on first time used the default password mentioned by you. The services come up fine, I install it and do a Ctrl+C to bring down the service. So all the MYSQL data is written into sql named volume.
When you do a Ctrl+C OR docker-compose down it only removes the containers and networks defined in the service. Not the volumes. Read more about it here
Now when you change password and bring the service back up it still uses the old volumes which has your old password.
So use a docker-compose down -v to remove the volumes too and give it a try.
Here are the steps how I reproduced it
Ctrl+C to stop all the services and then update the docker-compose.yml to update the password and do a docker-compose up again to get access denied error.
Do a docker-compose down -v to clean all the volume too and then do a docker-compose up
On doing a docker-compose down -v you will be losing all the data created by the prior service. Use it cautiously.
I want to set a sendmail_path in WordPress' container and use a sendmail provided by another container. In my case its MailHog.
So this is my docker-compose:
version: '2'
services:
wordpress:
image: wordpress
links:
- db:mysql
- mailhog
ports:
- 80:80
domainname: foo.com
hostname: foo
volumes:
- ./public:/var/www/html
environment:
WORDPRESS_DB_PASSWORD: example
depends_on:
- mailhog
mailhog:
image: mailhog/mailhog
ports:
- 1025:1025
- 8025:8025
db:
image: mariadb
environment:
MYSQL_ROOT_PASSWORD: example
I tried executing the command: "echo 'sendmail_path = \"/usr/local/bin/mailhog sendmail\"' > /usr/local/etc/php/conf.d/mail.ini" on WordPress container but it actually prints it...
Does these two have to share the volumes?
PS. I know I can use it as a SMTP server in the APP but I want to deal with it in more automated way.
You don't have MailHog installed in the WordPress container, so the path /usr/local/bin/mailhog doesn't exist.
What you want to do is to send emails via sendmail and those emails must be caught by MailHog. To do this, you must extend the WordPress Dockerfile:
FROM wordpress
RUN curl --location --output /usr/local/bin/mhsendmail https://github.com/mailhog/mhsendmail/releases/download/v0.2.0/mhsendmail_linux_amd64 && \
chmod +x /usr/local/bin/mhsendmail
RUN echo 'sendmail_path="/usr/local/bin/mhsendmail --smtp-addr=mailhog:1025 --from=no-reply#docker.dev"' > /usr/local/etc/php/conf.d/mailhog.ini
Note the --smtp-addr parameter must be in the form <mailhog_hostname>:<mailhog_port>.
Change your docker-compose.yml to build your Dockerfile.
version: '2'
services:
wordpress:
build:
context: ./
dockerfile: ./Dockerfile
links:
- db:mysql
- mailhog
ports:
- 80:80
domainname: foo.com
hostname: foo
volumes:
- ./public:/var/www/html
environment:
WORDPRESS_DB_PASSWORD: example
depends_on:
- mailhog
mailhog:
image: mailhog/mailhog
ports:
- 1025:1025
- 8025:8025
db:
image: mariadb
environment:
MYSQL_ROOT_PASSWORD: example
In this example, the Dockerfile you have written must be named "Dockerfile" and must be in the current directory (where you run docker-compose). You can change the path accordingly. You can remove the 1025:1025 ports entry if you don't need to connect to it from the host.
Now the function mail() should work as intended.