Quick reference for common Docker Compose operations with the phpMyFAQ production deployment described in Production Deployment.
The commands assume:
- the files
docker-compose.prod.ymland.envare in a directory calledphpmyfaq, so the Compose project name isphpmyfaq - the profiles
apacheandmariadbare enabled inCOMPOSE_PROFILES
The services and containers of docker-compose.prod.yml are:
| Profile | Service | Container |
|---|---|---|
apache |
apache |
phpmyfaq-apache |
frankenphp |
frankenphp |
phpmyfaq-frankenphp |
mariadb |
mariadb |
phpmyfaq-mariadb |
postgres |
postgres |
phpmyfaq-postgres |
elasticsearch |
elasticsearch |
phpmyfaq-elasticsearch |
opensearch |
opensearch |
phpmyfaq-opensearch |
If you use FrankenPHP or PostgreSQL, replace the names accordingly. Compose prefixes volume and network names with the
project name: the volume phpmyfaq_data is called phpmyfaq_phpmyfaq_data, the network phpmyfaq is called
phpmyfaq_phpmyfaq. docker volume ls shows the exact names.
To omit -f docker-compose.prod.yml in all commands, set it once in your shell:
export COMPOSE_FILE=docker-compose.prod.yml# Start all services of the profiles in COMPOSE_PROFILES
docker compose -f docker-compose.prod.yml up -d
# Start specific service
docker compose -f docker-compose.prod.yml up -d apache
# Start with other profiles than in .env
docker compose -f docker-compose.prod.yml --profile frankenphp --profile postgres up -d# Stop all services
docker compose -f docker-compose.prod.yml stop
# Stop specific service
docker compose -f docker-compose.prod.yml stop apache
# Stop and remove containers
docker compose -f docker-compose.prod.yml down
# Stop and remove containers + volumes (DANGEROUS - deletes the FAQ data and the database!)
docker compose -f docker-compose.prod.yml down -v# Restart all services
docker compose -f docker-compose.prod.yml restart
# Restart specific service
docker compose -f docker-compose.prod.yml restart apache# List all containers with their health status
docker compose -f docker-compose.prod.yml ps
# Show resource usage
docker stats
# Show container details
docker inspect phpmyfaq-apache# All services (follow mode)
docker compose -f docker-compose.prod.yml logs -f
# Specific service
docker compose -f docker-compose.prod.yml logs -f apache
# Last 100 lines
docker compose -f docker-compose.prod.yml logs --tail=100 apache
# Logs of the last hour
docker compose -f docker-compose.prod.yml logs --since 1h
# Save logs to file
docker compose -f docker-compose.prod.yml logs > phpmyfaq-logs.txtThe web server writes its access and error logs to the container output. The application logs of phpMyFAQ are
stored in the content/core/logs directory (volume phpmyfaq_logs).
Enable the maintenance mode and make a backup first, then open /update/ after the containers were recreated. See
Updates in the deployment guide for the complete procedure.
# Pull the images of the version set in PMF_VERSION
docker compose -f docker-compose.prod.yml pull
# Update and restart
docker compose -f docker-compose.prod.yml pull && \
docker compose -f docker-compose.prod.yml up -d
# Force recreate containers
docker compose -f docker-compose.prod.yml up -d --force-recreatedocker-compose.prod.yml only uses published images, it does not build them. To build your own image, see
Building the image yourself in the deployment guide.
# MariaDB backup
docker exec phpmyfaq-mariadb sh -c 'mariadb-dump -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE"' \
> backup.sql
# PostgreSQL backup
docker exec phpmyfaq-postgres pg_dump -U phpmyfaq phpmyfaq > backup.sql
# Compressed backup
docker exec phpmyfaq-mariadb sh -c 'mariadb-dump -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE"' \
| gzip > backup.sql.gz# MariaDB restore
docker exec -i phpmyfaq-mariadb sh -c 'mariadb -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE"' \
< backup.sql
# PostgreSQL restore
docker exec -i phpmyfaq-postgres psql -U phpmyfaq -d phpmyfaq < backup.sql
# From compressed backup
gunzip < backup.sql.gz | docker exec -i phpmyfaq-mariadb \
sh -c 'mariadb -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE"'# MariaDB shell
docker exec -it phpmyfaq-mariadb mariadb -u phpmyfaq -p phpmyfaq
# PostgreSQL shell
docker exec -it phpmyfaq-postgres psql -U phpmyfaq -d phpmyfaq
# Execute SQL query
docker exec phpmyfaq-mariadb \
sh -c 'mariadb -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE" -e "SHOW TABLES;"'The examples use the default user and database name phpmyfaq; adjust them if you changed MYSQL_USER,
MYSQL_DATABASE, POSTGRES_USER, or POSTGRES_DB in .env.
| Volume | Path in the container |
|---|---|
phpmyfaq_config |
/var/www/html/content/core/config |
phpmyfaq_data |
/var/www/html/content/core/data |
phpmyfaq_logs |
/var/www/html/content/core/logs |
phpmyfaq_attachments |
/var/www/html/content/user/attachments |
phpmyfaq_images |
/var/www/html/content/user/images |
The volumes caddy_data and caddy_config hold the certificates and the Caddy state of the FrankenPHP container,
mariadb_data, postgres_data, elasticsearch_data, and opensearch_data the files of the database and the search
engine. Back up the database with a dump instead of copying its volume.
# List all volumes
docker volume ls
# Filter phpMyFAQ volumes
docker volume ls | grep phpmyfaq# View volume details
docker volume inspect phpmyfaq_phpmyfaq_data
# Find volume location
docker volume inspect --format '{{ .Mountpoint }}' phpmyfaq_phpmyfaq_data# Backup data volume
docker run --rm \
-v phpmyfaq_phpmyfaq_data:/source:ro \
-v "$(pwd)":/backup \
alpine tar czf /backup/data-backup.tar.gz -C /source .
# Backup all phpMyFAQ volumes
mkdir -p backups
for vol in config data logs attachments images; do
docker run --rm \
-v "phpmyfaq_phpmyfaq_${vol}:/source:ro" \
-v "$(pwd)/backups":/backup \
alpine tar czf "/backup/${vol}-$(date +%Y%m%d).tar.gz" -C /source .
doneThe complete backup script for the database and the volumes is part of the deployment guide, see Backup and Restore.
# Stop the web container first
docker compose -f docker-compose.prod.yml stop apache
# Restore data volume
docker run --rm \
-v phpmyfaq_phpmyfaq_data:/target \
-v "$(pwd)":/backup \
alpine tar xzf /backup/data-backup.tar.gz -C /target
docker compose -f docker-compose.prod.yml start apache# Remove specific volume (DANGEROUS!)
docker volume rm phpmyfaq_phpmyfaq_data
# Remove unused anonymous volumes
docker volume prune# Open bash shell
docker exec -it phpmyfaq-apache bash
# Run command
docker exec phpmyfaq-apache ls -la /var/www/html
# Run as different user
docker exec -u www-data phpmyfaq-apache whoami
# Run the phpMyFAQ console, the mail queue worker or the task scheduler
docker exec -u www-data phpmyfaq-apache php /var/www/bin/console list
docker exec -u www-data phpmyfaq-apache php /var/www/bin/worker.php
docker exec -u www-data phpmyfaq-apache php /var/www/bin/scheduler.php run# Copy file from container
docker cp phpmyfaq-apache:/var/www/html/content/core/config/database.php ./database.php
# Copy file to container
docker cp local-file.txt phpmyfaq-apache:/var/www/html/content/user/images/
# List the application logs
docker exec phpmyfaq-apache ls -la /var/www/html/content/core/logsOnly files below content/ are kept in volumes. Changes to all other files are lost when the container is recreated.
# List permissions
docker exec phpmyfaq-apache ls -la /var/www/html/content/core
# Fix permissions (the entrypoint also does this on every start)
docker exec phpmyfaq-apache chown -R www-data:www-data /var/www/html/content# List all networks
docker network ls
# Inspect network
docker network inspect phpmyfaq_phpmyfaq# Check DNS resolution between containers
docker exec phpmyfaq-apache getent hosts mariadb
# Test database connection
docker exec phpmyfaq-apache php -r 'var_dump((bool) @fsockopen("mariadb", 3306));'
# Test search engine connection
docker exec phpmyfaq-apache curl -fsS http://elasticsearch:9200/_cluster/health# View health status
docker inspect --format '{{json .State.Health}}' phpmyfaq-apache
# Watch health status
watch -n 5 "docker inspect --format '{{.State.Health.Status}}' phpmyfaq-apache"
# All health statuses
docker compose -f docker-compose.prod.yml ps# Check web server and application (port PMF_HTTP_PORT, default 80)
curl -f http://localhost/api/health
# Check database
docker exec phpmyfaq-mariadb healthcheck.sh --connect --innodb_initialized
# Check Elasticsearch (not published to the host, so check from inside the container)
docker exec phpmyfaq-elasticsearch curl -fs http://localhost:9200/_cluster/health# Remove all stopped containers
docker container prune
# Remove specific stopped container
docker rm phpmyfaq-apache# Check disk usage before cleanup
docker system df
# Remove unused data
docker system prune
# Remove all unused images and anonymous volumes as well (DANGEROUS!)
docker system prune -a --volumes# Stop and remove containers
docker compose -f docker-compose.prod.yml down
# Remove volumes (DANGEROUS - you'll lose all data!)
docker compose -f docker-compose.prod.yml down -v
# Remove images
docker rmi ghcr.io/thorsten/phpmyfaq:latest
docker rmi mariadb:11.8
docker rmi elasticsearch:8.16.5# Restart unhealthy container
docker compose -f docker-compose.prod.yml restart apache
# View container processes
docker top phpmyfaq-apache
# View container resource limits
docker inspect --format '{{json .HostConfig.Resources}}' phpmyfaq-apache
# Check exit code
docker inspect --format '{{.State.ExitCode}}' phpmyfaq-apache# Show the resolved compose configuration (with the values from .env)
docker compose -f docker-compose.prod.yml config
# Validate compose file
docker compose -f docker-compose.prod.yml config --quiet# Live resource monitoring
docker stats --no-stream
# Check container events
docker events --filter container=phpmyfaq-apache
# Analyze image layers
docker history ghcr.io/thorsten/phpmyfaq:latest# Show all environment variables
docker exec phpmyfaq-apache env
# Search for specific variable
docker exec phpmyfaq-apache env | grep PMF_DBThe output contains passwords, so don't save it to files or share it.
# Edit .env file
nano .env
# Recreate containers with new env
docker compose -f docker-compose.prod.yml up -d --force-recreateThe PMF_DB_*, PMF_ADMIN_*, and PMF_BASE_URL variables are only read for the headless installation. Changing them
later has no effect on an existing installation.
# Export image
docker save ghcr.io/thorsten/phpmyfaq:latest | gzip > phpmyfaq-image.tar.gz
# Import image
gunzip < phpmyfaq-image.tar.gz | docker load
# Transfer between servers
docker save ghcr.io/thorsten/phpmyfaq:latest | ssh user@server 'docker load'Add to ~/.bashrc or ~/.zshrc:
# phpMyFAQ Docker aliases
alias pmf-up='docker compose -f docker-compose.prod.yml up -d'
alias pmf-down='docker compose -f docker-compose.prod.yml down'
alias pmf-restart='docker compose -f docker-compose.prod.yml restart'
alias pmf-logs='docker compose -f docker-compose.prod.yml logs -f'
alias pmf-ps='docker compose -f docker-compose.prod.yml ps'
alias pmf-update='docker compose -f docker-compose.prod.yml pull && docker compose -f docker-compose.prod.yml up -d'
alias pmf-shell='docker exec -it phpmyfaq-apache bash'
alias pmf-db='docker exec -it phpmyfaq-mariadb mariadb -u phpmyfaq -p phpmyfaq'
alias pmf-backup='./backup.sh' # the backup script from the deployment guidePortainer deploys the stack from its web interface, see Deployment with Portainer in the deployment guide. Set
COMPOSE_PROFILES as a stack environment variable there.
- Use the GUI to view containers, logs, and stats
- Access terminal directly from container list
- Volume management in the Volumes section
# Start Docker on boot; the containers come back thanks to "restart: unless-stopped"
sudo systemctl enable docker
sudo systemctl start dockerWhen something goes wrong:
-
Check if containers are running
docker compose -f docker-compose.prod.yml ps
-
Check logs for errors
docker compose -f docker-compose.prod.yml logs --tail=50
-
Verify network connectivity
docker exec phpmyfaq-apache getent hosts mariadb -
Check disk space
docker system df
-
Verify environment variables
docker exec phpmyfaq-apache env | grep PMF
-
Test database connection
docker exec phpmyfaq-mariadb healthcheck.sh --connect -
Check file permissions
docker exec phpmyfaq-apache ls -la /var/www/html/content/core -
Review resource usage
docker stats --no-stream
See Monitoring and Troubleshooting in the deployment guide for the common problems and their solutions.