Software Developer, Technology Enthusiast, Retro and Husband and Dad based in Melbourne.

Monthly Archives: August 2026

Securing WordPress with Docker and AWS Lightsail

Summary

In this chapter, we will improve the security of our WordPress stack by moving the database passwords out of the main docker-compose.yml file.

Instead of storing passwords directly inside the Compose file, we will use Docker Secrets in conjunction with Docker Swarm. This allows the mysql, wordpress, and wpcli services to read password values from files mounted inside the running containers.

Sample folder for this chapter:

cd wordpressawslightsailsamples/Securing_WordPress_with_Docker_and_AWS_Lightsail

This folder contains the docker-compose.yml file used throughout this chapter:

Understanding WordPress Security with Docker Compose

What Are Docker Secrets?

We will use Docker Secrets to improve the security of our WordPress stack.

Docker Secrets provide a secure and reliable way to manage sensitive information required by containers at runtime. This includes database passwords, usernames, and other credentials that should not be stored directly inside a docker-compose.yml file.


services:
  mysql:
    image: mysql:latest
    
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD_FILE: /run/secrets/mysql_password
    ports:
      - "3306:3306"
    secrets:
      - mysql_password
      - mysql_root_password
      
    volumes:
      - mysql_data:/var/lib/mysql
    networks:
      - wp-network
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure

  wordpress:
    depends_on:
       - mysql
    image: wordpress:latest
    environment:
       WORDPRESS_DB_HOST: mysql:3306
       WORDPRESS_DB_NAME: wordpress
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD_FILE: /run/secrets/mysql_password
     
       WORDPRESS_DEBUG: 1
      
    secrets:
      - mysql_password
    ports:
      - "80:80"
    volumes:
      - wp_html:/var/www/html
    networks:
      - wp-network
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure

  wpcli:
    image: wordpress:cli
    entrypoint: wp
    working_dir: /var/www/html
    volumes:
      - wp_html:/var/www/html
    environment:
      WORDPRESS_DB_HOST: mysql:3306
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD_FILE: /run/secrets/mysql_password
    secrets:
      - mysql_password
    networks:
      - wp-network
    deploy:
      replicas: 0

volumes:
  wp_html:
    external: true
  mysql_data:
    external: true

networks:
  wp-network:
   driver: overlay

secrets:
  mysql_root_password:
    external: true
  mysql_password:
    external: true

Docker Secrets require Docker Swarm when deploying services with docker stack deploy. Before we can use secrets in our WordPress stack, we need to make sure Swarm mode is enabled on our Docker server, whether it is running on Lightsail or Docker Desktop.

Docker Swarm is Docker’s built-in tool for managing and running containers as services, making it suitable for deploying and managing our WordPress stack.

1. Check Whether Docker Swarm Is Already Enabled

To determine whether the Docker host MyUbuntuInstance is already part of a Docker Swarm, run:

docker -H ssh://MyUbuntuInstance node ls

If Swarm mode has not yet been initialised, you may encounter an error similar to the following:

Error response from daemon: This node is not a swarm manager. Use "docker swarm init" or "docker swarm join" to connect this node to swarm and try again.

This indicates that Docker is running, but the server has not yet been configured as a Swarm manager.

2. Initialise Docker Swarm

To initialise Docker Swarm on the remote Docker host MyUbuntuInstance, run:

docker -H ssh://MyUbuntuInstance swarm init

Docker should return output similar to the following:

Swarm initialized: current node (nyj0pha0ecxkwqd7eld75tv0v) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-60tdhafh6cak85ol8n5lx4okr9fhfoc3kloncihy4hmdlyf2gw-0nxhnq8gpdau3su0g36ybasu8 172.26.2.42:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

This indicates that Swarm mode has been successfully enabled and that the current server is now functioning as the Swarm manager.

3. Check the Swarm Node List Again

Run the following command again to confirm that the node is now part of the Swarm:

docker -H ssh://MyUbuntuInstance node ls

You should see output similar to the following:

ID                            HOSTNAME         STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
nyj0pha0ecxkwqd7eld75tv0v *   ip-172-26-2-42   Ready     Active         Leader           28.4.0

4. Why Use Docker Secrets?

In the previous chapter, Docker Compose and WordPress, we stored the passwords directly inside the sample docker-compose.yml file:

MYSQL_ROOT_PASSWORD: wordpress
MYSQL_PASSWORD: wordpress
WORDPRESS_DB_PASSWORD: wordpress

While this approach is suitable for a simple example, storing passwords directly in the Compose file is not considered a best practice for secure configurations.

In this chapter, we will move these password values to Docker Secrets. Instead of including the passwords directly in docker-compose.yml, the services will retrieve them from secret files at runtime:

MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password
MYSQL_PASSWORD_FILE: /run/secrets/mysql_password
WORDPRESS_DB_PASSWORD_FILE: /run/secrets/mysql_password

5. Set Up the Docker Secrets

Before deploying the WordPress stack, we need to create the Docker Secrets that will be used by the MySQL and WordPress services.

We will use the docker secret create command to create each secret.

Create the Secrets in Docker

1. Create the mysql_password and mysql_root_password Secrets

When creating a password file for a MySQL Docker Secret, make sure the file does not contain a trailing carriage return or line-feed character. An extra newline becomes part of the password and can cause MySQL authentication errors when the secret is read.

Reference: Docker MySQL newline issue

mysql_password

Windows
Set-Content -Path .\mysql_password.txt -Value "wordpress" -NoNewline:$true
docker -H ssh://MyUbuntuInstance secret create mysql_password .\mysql_password.txt
macOS / Linux
printf '%s' 'wordpress' > mysql_password.txt
docker -H ssh://MyUbuntuInstance secret create mysql_password ./mysql_password.txt

mysql_root_password

Windows
Set-Content -Path .\mysql_root_password.txt -Value "wordpress" -NoNewline:$true
docker -H ssh://MyUbuntuInstance secret create mysql_root_password .\mysql_root_password.txt
macOS / Linux
printf '%s' 'wordpress' > mysql_root_password.txt
docker -H ssh://MyUbuntuInstance secret create mysql_root_password ./mysql_root_password.txt

2. List the Secrets

docker -H ssh://MyUbuntuInstance secret ls
ID                          NAME                  DRIVER    CREATED       UPDATED
z7w3jg9l8mascak2h46i073g0   mysql_password                  4 weeks ago   4 weeks ago
ucu7gnnsaw41wdrt8n2dcdpgd   mysql_root_password             4 weeks ago   4 weeks ago

3. Stop the Docker Compose Stack from Docker Compose and WordPress

Before deploying the updated WordPress stack that uses Docker Secrets, stop the Docker Compose stack created in the previous chapter, Docker Compose and WordPress.

Change to the sample directory containing the previous docker-compose.yml file:

cd wordpressawslightsailsamples/Docker_Compose_and_Wordpress

Shut down and remove the containers and networks created by that Compose project:

docker -H ssh://MyUbuntuInstance compose down

The named volumes remain in place, so your WordPress files and MySQL database data can be used again when you deploy the updated stack.

4. Deploy the New WordPress Stack

Change to the sample directory that contains the new docker-compose.yml file:

cd wordpressawslightsailsamples/Securing_WordPress_with_Docker_and_Lightsail

Deploy the stack:

docker -H ssh://MyUbuntuInstance stack deploy -c docker-compose.yml wordpress-stack

Options Explained

  • docker runs the Docker command-line interface.
  • -H ssh://MyUbuntuInstance connects Docker to the remote Lightsail instance over SSH.
  • stack deploy creates a new Docker Swarm stack or updates an existing stack.
  • -c docker-compose.yml specifies the Docker Compose file that defines the WordPress services, networks, volumes, and secrets.
  • wordpress-stack specifies the Docker stack name used as a prefix for services, networks, and other deployment resources.

5. Verify the WordPress Stack Services

List the services deployed as part of wordpress-stack:

docker -H ssh://MyUbuntuInstance stack services wordpress-stack

Options Explained

  • docker runs the Docker command-line interface.
  • -H ssh://MyUbuntuInstance connects Docker to the remote Lightsail instance over SSH.
  • stack services lists the services deployed within the specified stack.
  • wordpress-stack specifies the name of the stack.

The command shows details for each service, including its name, replica status, container image, and published ports.

ID             NAME                        MODE         REPLICAS   IMAGE              PORTS
17zb0ywotjss   wordpress-stack_mysql       replicated   1/1        mysql:latest       *:3306->3306/tcp
v0v2ameyqd2x   wordpress-stack_wordpress   replicated   1/1        wordpress:latest   *:80->80/tcp
f0jxrzcv873r   wordpress-stack_wpcli       replicated   0/0        wordpress:cli

6. Retrieve the Lightsail Static IP Address

Using the AWS CLI, you can retrieve the current static IP address assigned to the Lightsail instance MyUbuntuInstance:

aws lightsail get-static-ip --static-ip-name MyUbuntuInstanceStaticIP --region ap-southeast-2 --profile MyUbuntuProfile

Access WordPress Remotely

Once the containers are running, open a web browser and go to:

http://ipAddress

Replace ipAddress with the static IP address returned by the AWS CLI command.

7. Stopping the WordPress Stack with Docker Stack

To gracefully remove the WordPress stack from the remote Lightsail instance, including its associated services and containers, run:

docker -H ssh://MyUbuntuInstance stack rm wordpress-stack

Further Reading

Using WordPress on AWS Lightsail and Docker