Guide to Deploy Multiple WordPress Sites on EC2 with Podman
Deploying Multiple WordPress Sites on EC2 Using Podman: Step-by-Step Guide
Table of Contents
- Background
- Possible Architecture
- Setup EC2 and Podman
- Creating Configuration Files for WordPress and MySQL
- Creating Podman Compose File and Environment Variables
- Testing and installation of Wordpress
- Setting Up ACM (AWS Certificate Manager)
- Setting Up ALB (Application Load Balancer)
- Setting Up Security Group for the EC2
- Conclusion and Summary
1. Background
Podman is a lightweight, open-source container management tool that runs containers without a background daemon. Its efficiency and security make it ideal for deploying containerized applications on AWS EC2. It is especially good for small to medium-sized enterprises (SMEs) seeking a cost-effective solution. In this guide, I’ll use Podman to deploy multiple WordPress sites on an Ubuntu EC2 instance.
Reasons to Use Podman:
Podman offers several key benefits:
- Resource Efficiency: It has a lower memory footprint, making it ideal for environments with limited resources.
- Security: Podman runs containers as non-root users by default. This enhances security.
- Compatibility: Compatible with Docker CLI for a smooth transition.
Ubuntu is chosen for this setup due to its ease of use and straightforward installation process for Podman. While Amazon Linux is also an option, Docker is generally recommended for that environment. This setup is particularly suitable for SMEs with limited resources who prefer not to deal with the complexities of ECS or EKS.
2. Possible Architecture
The architecture involves an EC2 instance running Ubuntu with Podman, hosting multiple WordPress sites. An Application Load Balancer (ALB) with ACM (AWS Certificate Manager) can be used to manage traffic and SSL certificates. The EC2 instance is placed in a private subnet for enhanced security.
High-Level Architecture Diagram
- EC2 Instance: Hosts multiple WordPress sites using Podman.
- ALB: Distributes incoming traffic across multiple WordPress sites.
- ACM: Manages SSL certificates for secure HTTPS connections.
The setup involves three containers: 1 MYSQL DB container and 2 wordpress site containers

3. Setup EC2 and Podman
The first step is to launch an EC2 Instance:
- Log in to the AWS Management Console.
- Navigate to the EC2 Dashboard and launch a new instance.
- Select Ubuntu as the operating system.
- Choose an instance type based on your resource requirements (e.g., t2.micro for small setups).
- Configure security groups to allow custom port 8001 and 8002 (for both website), and SSH (port 22) traffic.
Installing Podman and Podman-Compose
Once the EC2 instance is up and running, SSH into the instance (or Use EC2 Instance Connnect) and install Podman:
sudo apt update sudo apt install podman sudo apt install podman-compose
4. Creating Configuration Files for WordPress and MySQL
Directory Structure Setup
Create a new directory for your project and set up the necessary subdirectories:
mkdir wp_server mkdir -p /mysql/docker-entrypoint-initdb.d mysql/init mysql/conf.d wp-content/site1 wp-content/site2 php
MySQL Initialization Script
Create an SQL file to initialize the MySQL database:
nano ./mysql/docker-entrypoint-initdb.d/init.sql
Add the following content to the file:
-- Wait for MySQL to be ready SELECT 1; -- Create databases CREATE DATABASE IF NOT EXISTS wordpress_site1; CREATE DATABASE IF NOT EXISTS wordpress_site2; -- Drop users if they exist DROP USER IF EXISTS 'user_site1'@'%'; DROP USER IF EXISTS 'user_site2'@'%'; -- Create users CREATE USER 'user_site1'@'%' IDENTIFIED BY 'password_site1'; CREATE USER 'user_site2'@'%' IDENTIFIED BY 'password_site2'; -- Grant privileges GRANT ALL PRIVILEGES ON wordpress_site1.* TO 'user_site1'@'%'; GRANT ALL PRIVILEGES ON wordpress_site2.* TO 'user_site2'@'%'; FLUSH PRIVILEGES;
Set the proper permissions:
chmod 644 mysql/init/init.sql chmod 755 mysql/init
MySQL Configuration File
Create a MySQL configuration file:
nano ./mysql/conf.d/my.cnf
Add any custom MySQL configurations as needed.
[mysqld] character-set-server = utf8mb4 collation-server = utf8mb4_unicode_ci max_allowed_packet = 64M innodb_buffer_pool_size = 256M innodb_log_file_size = 64M
PHP Configuration File
Create a PHP configuration file for both WordPress sites:
nano ./php/php.ini
Add the following content:
upload_max_filesize = 64M post_max_size = 64M max_execution_time = 300 memory_limit = 256M
5. Creating Podman Compose File and Environment Variables
Create a docker-compose.yml
file. This create container a MYSQL database and two wordpress website in PHP.
version: '3.8' services: db: image: docker.io/mysql:8.0 restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} volumes: - db_data:/var/lib/mysql:Z - ./mysql/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d:Z - ./mysql/conf.d:/etc/mysql/conf.d:Z networks: - wordpress_network command: --default-authentication-plugin=mysql_native_password healthcheck: test: mysqladmin ping -h localhost -u root -p${MYSQL_ROOT_PASSWORD} interval: 10s timeout: 5s retries: 5 start_period: 30s wordpress_site1: image: docker.io/wordpress:${WP_VERSION} restart: unless-stopped depends_on: db: condition: service_healthy ports: - "${WP_PORT_SITE1}:80" environment: WORDPRESS_DB_HOST: db WORDPRESS_DB_USER: ${MYSQL_USER_SITE1} WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD_SITE1} WORDPRESS_DB_NAME: ${MYSQL_DATABASE_SITE1} WORDPRESS_TABLE_PREFIX: wp1_ volumes: - wordpress_site1_data:/var/www/html:Z - ./wp-content/site1:/var/www/html/wp-content:Z - ./php:/usr/local/etc/php/conf.d:Z networks: - wordpress_network healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:80 || exit 1"] interval: 30s timeout: 10s retries: 3 start_period: 30s wordpress_site2: image: docker.io/wordpress:${WP_VERSION} restart: unless-stopped depends_on: db: condition: service_healthy ports: - "${WP_PORT_SITE2}:80" environment: WORDPRESS_DB_HOST: db WORDPRESS_DB_USER: ${MYSQL_USER_SITE2} WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD_SITE2} WORDPRESS_DB_NAME: ${MYSQL_DATABASE_SITE2} WORDPRESS_TABLE_PREFIX: wp2_ volumes: - wordpress_site2_data:/var/www/html:Z - ./wp-content/site2:/var/www/html/wp-content:Z - ./php:/usr/local/etc/php/conf.d:Z networks: - wordpress_network healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:80 || exit 1"] interval: 30s timeout: 10s retries: 3 start_period: 30s networks: wordpress_network: driver: bridge volumes: db_data: wordpress_site1_data: wordpress_site2_data:
Environment Variables Setup
Create an .env
file to store environment variables:
nano .env
Add the following content:
# MySQL Settings MYSQL_ROOT_PASSWORD=rootpassword # Site 1 MYSQL_USER_SITE1=user_site1 MYSQL_PASSWORD_SITE1=password_site1 MYSQL_DATABASE_SITE1=wordpress_site1 WP_PORT_SITE1=8001 # Site 2 MYSQL_USER_SITE2=user_site2 MYSQL_PASSWORD_SITE2=password_site2 MYSQL_DATABASE_SITE2=wordpress_site2 WP_PORT_SITE2=8002 # WordPress Version WP_VERSION=latest
Podman Startup Script
Create a startup script to automate the container setup:
nano podman-startup.sh
Add the following content:
#!/bin/bash
# Start services
echo "Starting services..."
podman-compose up -d db
# Wait for MySQL to be healthy
echo "Waiting for MySQL to be ready..."
while ! podman-compose exec db mysqladmin ping -h"localhost" --silent; do
sleep 1
done
# Start WordPress containers
echo "Starting WordPress containers..."
podman-compose up -d wordpress_site1 wordpress_site2
# Wait for WordPress containers
echo "Waiting for WordPress to be ready..."
sleep 30
# Check services
echo "Checking services..."
podman-compose ps
echo "Setup complete! Access your sites at:"
echo "Site 1: http://localhost:${WP_PORT_SITE1}"
echo "Site 2: http://localhost:${WP_PORT_SITE2}"
Make the script executable and run the script:
chmod +x podman-startup.sh
./podman-startup.sh
Setting Up SWAP Space (Optional)
If your EC2 instance has limited memory, you can add SWAP space to prevent out-of-memory errors:
sudo fallocate -l 3G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile swap swap defaults 0 0' | sudo tee -a /etc/fstab
Adjusting Swappiness Value
To reduce the usage of SWAP space, adjust the swappiness value:
# Check current swappiness
cat /proc/sys/vm/swappiness
# Set lower swappiness (temporarily)
sudo sysctl vm.swappiness=10
# Make it permanent
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
6. Testing and installation of Wordpress
Testing the Setup of WordPress on EC2 with Podman
Once you have completed the setup of your WordPress sites using Podman on the EC2 instance, it’s time to test the installation and ensure everything is working as expected. Follow these steps to verify the setup:
1. Check Running Containers
Run the following command to check if the containers for MySQL and WordPress are running:
podman ps
You should see output similar to this:

- Ensure that the db, wordpress_site1, and wordpress_site2 containers are running.
- Verify that the ports are correctly mapped (e.g.,
8001
and8002
for the WordPress sites).
2. Access WordPress Installation Screen
Open a web browser and navigate to the following URLs to access the WordPress installation screens:
- Site 1:
http://<EC2_Public_IP>:8001
- Site 2:
http://<EC2_Public_IP>:8002
You should see the WordPress installation screen for both sites in the browser, which looks like this. Then, you can follow the wizard to setup the website.
3. Verify Both WordPress Sites
Once the setup is complete, verify that both WordPress sites are accessible and functioning correctly:
- Site 1: Navigate to
http://<EC2_Public_IP>:8001
to view the first WordPress site. - Site 2: Navigate to
http://<EC2_Public_IP>:8002
to view the second WordPress site.
You should see the default WordPress theme and content on both sites. For example:
- Welcome to WordPress. This is your first post. Edit or delete it, then start writing!

Both sites should now be up and running having separate content and configurations.
Some Troubleshooting Tips
- Containers Not Running: If the containers are not running, check the logs using
podman logs <container_name>
to identify any issues. - WordPress Installation Fails: If the WordPress installation fails, ensure that the MySQL database is running and accessible. Check the database credentials in the
.env
file.
By following these steps, you can successfully test and verify the setup of multiple WordPress sites on an EC2 instance using Podman.
7. Setting Up ACM (AWS Certificate Manager)
You have setup both wordpress sites run on IP. In the following sections (7, 8 and 9), I will go through the steps to allow you to enter both sites by your domain names (e.g. example.com) with HTTPS connection
Only High-Level Steps Provided:
- Request a Certificate:
- Navigate to the AWS Certificate Manager (ACM) in the AWS Management Console.
- Click on Request a certificate.
- Choose Request a public certificate and click Next.
- Enter the domain names for your WordPress sites (e.g.,
example.com
,www.example.com
). - Select DNS validation as the validation method and click Request.
- Validate the Certificate:
- After requesting the certificate, ACM will provide you with DNS records (CNAMEs) that need to be added to your domain's DNS configuration.
- Go to your domain registrar or DNS provider (e.g., Route 53 or GoDaddy) and add the provided CNAME records.
- Once the DNS records are propagated, ACM will validate the domain and issue the certificate.
- Associate the Certificate with ALB:
- After the certificate is issued, you can associate it with your Application Load Balancer (ALB) during the ALB setup process (covered in the next section).
8. Setting Up ALB (Application Load Balancer)
This section describes the steps to allow you to use ALB as reverse proxy so that traffic is route to different websites based on the host header.
Only High-Level Steps Provided:
- Create the Load Balancer:
- Go to the EC2 Dashboard in the AWS Management Console.
- Under Load Balancing, click on Load Balancers.
- Click Create Load Balancer and select Application Load Balancer.
- Configure the load balancer:
- Set a name for the load balancer.
- Choose Internet-facing as the scheme.
- Select the appropriate VPC and subnets (ensure you select both public and private subnets if applicable).
- Configure security groups to allow HTTP (port 80) and HTTPS (port 443) traffic.
- Configure Listeners:
- Add a listener for HTTP (port 80) and another for HTTPS (port 443).
- For the HTTPS listener, select the ACM certificate you created earlier.
- Optionally, you can redirect HTTP traffic to HTTPS for better security.
- Configure Two Target Groups:
- Create a target group for each WordPress site (port 8001 and port 8002).
- Set the target type to Instance.
- Register the EC2 instance(s) running your WordPress sites as targets.
- Configure health checks to ensure the load balancer routes traffic only to healthy instances.
- Configure Listener Rules (HTTPS:443) of the ALB:
- Set up routing rules to direct traffic to the appropriate target group based on the "HTTP HOST HEADER" condition.
- By settng the action of the rules, you can route traffic for
site1.example.com
to the target group for WordPress Site 1 andsite2.example.com
to the target group for WordPress Site 2.
- Test the Load Balancer:
- Once the ALB is set up, test it by accessing your WordPress sites using the ALB's DNS name.
- Ensure that HTTPS is working correctly and that traffic is being routed to the correct WordPress sites.

Additional Troubleshooting
If you are redirected to the wrong port (e.g., https://site1.example.com:8001), this may be due to an incorrect setup of the WordPress site1. You can try login and try to update the relevant record in the MySQL database to resolve this issue.
podman exec -it wp_server_db_1 bash
mysql -u root -prootpassword
use wordpress_site1;
-- Check current WordPress URLs
SELECT option_name, option_value
FROM wp1_options
WHERE option_name IN ('siteurl', 'home');
-- Update the URLs if needed
UPDATE wp1_options
SET option_value = 'https://site1.example.com'
WHERE option_name IN ('siteurl', 'home');
9. Setting Up Security Group for the EC2
Only High-Level Steps Provided:
- Create a Security Group (Alternatively, you can modify an existing security group in step 3 if one is already available):
- Go to the EC2 Dashboard in the AWS Management Console.
- Under Network & Security, click on Security Groups.
- Click Create Security Group.
- Provide a name and description for the security group (e.g.,
WordPress-EC2-SG
). - Select the VPC where your EC2 instance is located.
- Configure Inbound Rules:
- Add inbound rules to allow specific types of traffic to your EC2 instance:
- SSH (Port 22): Allow SSH access from your IP address or a specific IP range for secure remote management.
- Custom TCP (Podman Port 8001): Allow traffic from the security group of Application Load Balancer (ALB)
- Custom TCP (Podman Port 8002): Allow traffic from the security group of Application Load Balancer (ALB)
- Add inbound rules to allow specific types of traffic to your EC2 instance:
- Configure Outbound Rules:
- By default, outbound traffic is allowed to all destinations. However, you can restrict outbound traffic if needed:
- Allow outbound traffic to the internet (e.g., for WordPress updates, plugin installations, etc.).
- By default, outbound traffic is allowed to all destinations. However, you can restrict outbound traffic if needed:
- Associate the Security Group with the EC2 Instance (if you created a new security group):
- Go to the EC2 Instances section in the AWS Management Console.
- Select your EC2 instance and click Actions > Security > Change Security Groups.
- Attach the newly created security group (e.g.,
WordPress-EC2-SG
) to the instance. - Ensure that the security group is properly applied and that unnecessary security groups are removed.
- Test the Security Group:
- Verify that the security group is working as expected:
- SSH into the EC2 instance to ensure remote access is allowed.
- Access your WordPress sites via HTTP/HTTPS to ensure web traffic is allowed.
- Test any custom ports (e.g., 8001, 8002) to ensure they are accessible.
- Verify that the security group is working as expected:
10. Conclusion and Summary
Summary
In this guide, I have covered deploying multiple WordPress site on an single EC2 instance using Podman. The key steps included installing Podman, configuring WordPress and MySQL, and setting up a Podman Compose file to manage containers.
Some Recommendations on Security
- HTTPS: Remember to use AWS Certificate Manager (ACM) to manage SSL certificates
- CloudFront: Implement Amazon CloudFront as a CDN to improve performance, reduce latency, and add security features with Lambda@Edge. If possible, you should add AWS WAF for further protection.
- Security Groups: Restrict EC2 instances traffic to essential ports (e.g., HTTP, HTTPS, SSH).
Final Recommendation: ECS for Larger Websites
This setup works well for small to medium-sized sites, but larger or high-traffic websites should consider deploying on Amazon ECS or EKS. They offer better scalability, advanced security, and seamless integration with AWS services.