Scenario: Customer want to have entire LAMP stack in docker for better scalability, simple management, updates, etc.
Solution: This will install standard LAMP stack + phpMyAdmin + HTTPS with Let`s encrypt. (this specific configuration created for OJS 3.5. Modify it for your needs)
Solution directory structure:
lamp-docker/
│
├── docker-compose.yml
├── letsecrypt/
│ └── acme.json
└── php/
└── Dockerfile
└── vhost.conf
Docker-compose content:
networks:
lampnet:
driver: bridge
services:
traefik:
image: traefik:3.6
container_name: traefik
restart: always
command:
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--entrypoints.web.http.redirections.entryPoint.scheme=https"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
- "--certificatesresolvers.letsencrypt.acme.email=admin@your-domain.ca"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
- "--log.level=INFO"
- "--accesslog=true"
- "--accesslog.filepath=/var/log/traefik/access.log"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
- ./logs/traefik:/var/log
networks:
- lampnet
apache-php:
build: ./php
container_name: apache-php
restart: always
labels:
- "traefik.enable=true"
- "traefik.http.routers.ojs.rule=Host(`your-domain.ca`)"
- "traefik.http.routers.ojs.entrypoints=websecure"
- "traefik.http.routers.ojs.tls.certresolver=letsencrypt"
- "traefik.http.services.ojs.loadbalancer.server.port=80"
- "traefik.http.middlewares.ojs-headers.headers.customrequestheaders.X-Forwarded-Proto=https"
- "traefik.http.middlewares.ojs-headers.headers.customrequestheaders.X-Forwarded-Host=your-domain.ca"
- "traefik.http.routers.ojs.middlewares=ojs-headers"
volumes:
- ./www:/var/www/html
- ./logs/apache:/var/log/apache2
networks:
- lampnet
mariadb:
image: mariadb:11
container_name: mariadb
restart: always
environment:
MYSQL_ROOT_PASSWORD: root_superpassword
MYSQL_DATABASE: ojsdb
MYSQL_USER: ojsuser
MYSQL_PASSWORD: superpassword
command: --bind-address=0.0.0.0
ports:
- "127.0.0.1:3306:3306"
volumes:
- ./mariadb_data:/var/lib/mysql
networks:
- lampnet
phpmyadmin:
image: phpmyadmin/phpmyadmin:latest
container_name: phpmyadmin
restart: always
environment:
PMA_HOST: mariadb
PMA_PORT: 3306
labels:
- "traefik.enable=true"
- "traefik.http.routers.pma.rule=Host(`your-domain.ca`) && PathPrefix(`/myadmin`)"
- "traefik.http.routers.pma.entrypoints=websecure"
- "traefik.http.routers.pma.tls.certresolver=letsencrypt"
- "traefik.http.middlewares.pma-add-slash.redirectregex.regex=^(.*/myadmin)$"
- "traefik.http.middlewares.pma-add-slash.redirectregex.replacement=$1/"
- "traefik.http.middlewares.pma-add-slash.redirectregex.permanent=true"
- "traefik.http.middlewares.pma-stripprefix.stripprefix.prefixes=/myadmin"
- "traefik.http.routers.pma.middlewares=pma-add-slash,pma-stripprefix"
- "traefik.http.services.pma.loadbalancer.server.port=80"
volumes:
- ./logs/phpmyadmin:/var/log
networks:
- lampnet
Content of php/Dockerfile:
FROM php:8.3-apache
RUN apt-get update && apt-get install -y \
libicu-dev \
libxml2-dev \
libzip-dev \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
libonig-dev \
libbz2-dev \
unzip \
ftp \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install intl mbstring xml bcmath zip gd ftp pdo_mysql mysqli\
&& a2enmod rewrite \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Virtual host
COPY vhost.conf /etc/apache2/sites-available/000-default.conf
COPY custom-php.ini /usr/local/etc/php/conf.d/
# Set Apache log location to host-mounted volume
RUN mkdir -p /var/log/apache2
RUN chown -R www-data:www-data /var/www/html
content of php/vhost.conf (Apache virtual host).
<VirtualHost *:80>
ServerName your-domain.ca
DocumentRoot /var/www/html
ErrorLog /var/log/apache2/your-domain-error.log
CustomLog /var/log/apache2/your-domain-access.log combined
<Directory /var/www/html>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
content of php/custom-php.ini
memory_limit = 256M
upload_max_filesize = 50M
post_max_size = 50M
max_execution_time = 120
how to connect to DB from host:
install mysql-client on host.
use following connand to connect:
mysql --protocol=TCP -h 127.0.0.1 -P 3306 -u ojsuser -p
Script for operations. control.sh
#!/bin/bash
set -e
COMMAND=$1
show_help() {
echo "Usage: $0 {start|stop|build|restart|rebuild|status}"
echo
echo " start - Start all containers"
echo " stop - Stop all containers"
echo " build - Build local container images"
echo " restart - Stop then start containers"
echo " rebuild - Stop → rebuild images → start"
echo " status - Show running service list"
exit 1
}
if [ -z "$COMMAND" ]; then
show_help
fi
case "$COMMAND" in
start)
echo "---- Starting containers ----"
docker compose up -d
echo "---- Done ----"
;;
stop)
echo "---- Stopping containers ----"
docker compose down
echo "---- Done ----"
;;
build)
echo "---- Building container images ----"
docker compose build
echo "---- Done ----"
;;
restart)
echo "---- Restarting containers ----"
docker compose down
docker compose up -d
echo "---- Done ----"
;;
rebuild)
echo "---- Stopping containers ----"
docker compose down
echo "---- Rebuilding images ----"
docker compose build --no-cache
echo "---- Starting containers ----"
docker compose up -d
echo "---- Done ----"
;;
status)
echo "---- Container status ----"
docker compose ps
;;
*)
echo "Error: Unknown command '$COMMAND'"
show_help
;;
esac
Start containers with script. Build process depends on your system and internet connection. In my case it took around 5 min to build containers and start services.
Done.