Docker based environment for new Pimcore installations.
Directory Structure
- docker-compose.yml — all service definitions
- docker/ — Docker build context and configuration files
- php/
- docker file — PHP 8.4-FPM custom image
- pimcore-supervisor.conf — Definition of supervisory worker
- nginx/
- default.conf — Pimcore HTTPS vhost (port 443)
- certificate/ — SSL Certificate (
pimcore.local.crt/.key) - php.ini — PHP runtime configuration
- data/ — persistent volume data (git ignored)
- mysql/ — MySQL database files
- redis/ — Redis AOF/RDB snapshots
- open search/ — OpenSearch index data
- application/ — Application source Pimcore (Symfony)
- .env — default environment variables (committed)
- .env.local — local replacement (not performed)
- .docker/
- supervisord.conf — optional Supervisord replacement
Service
- php (
pimcore_php) — Custom Dockerfile — PHP 8.4-FPM + Supervisor Worker - nginx (
pimcore_nginx) —nginx:stable— HTTPS reverse proxy - db (
pimcore_db) —mysql:8.0— Application database - redis (
pimcore_redis) —redis:7-alpine— Cache / session (2 GB LRU) - rabbitmq (
pimcore_rabbitmq) —rabbitmq:3-management— Message broker - open search (
pimcore_opensearch) —opensearchproject/opensearch:2.11.0— Full text search - Mercure (
pimcore_mercure) —dunglas/mercure— Real time SSE hub
All containers share a named bridge network pimcore. Service hostname (db, redisetc.) are resolved automatically in it.
Harbor
- 443 — Nginx — HTTPS Pimcore
- 5672 — RabbitMQ — AMQP
- 15672 — RabbitMQ — Management UI (
guest/guest) - 9201 — Open Search — REST API
Configuration File
docker-compose.yml
services:
php:
build:
context: ./docker
dockerfile: php/Dockerfile
container_name: pimcore_php
command: >
bash -c "
mkdir -p /var/www/html/var /var/www/html/public/var &&
chown -R www-data:www-data /var/www/html/var /var/www/html/public/var &&
chmod -R 775 /var/www/html/var /var/www/html/public/var &&
/usr/bin/supervisord -n -c /etc/supervisor/supervisord.conf
"
volumes:
- ./app:/var/www/html
- ./docker/php.ini:/usr/local/etc/php/php.ini:ro
- ./app/.docker/supervisord.conf:/etc/supervisor/conf.d/pimcore.conf:ro
depends_on: [db, redis, opensearch]
environment:
- APP_ENV=prod
networks: [pimcore]
nginx:
image: nginx:stable
container_name: pimcore_nginx
ports:
- "443:443"
volumes:
- ./app:/var/www/html
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
- ./docker/nginx/certs:/etc/nginx/certs:ro
depends_on: [php]
networks: [pimcore]
db:
image: mysql:8.0
container_name: pimcore_db
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: pimcore
MYSQL_USER: pimcore
MYSQL_PASSWORD: pimcore
volumes:
- ./docker/data/mysql:/var/lib/mysql
networks: [pimcore]
redis:
image: redis:7-alpine
container_name: pimcore_redis
command: redis-server --maxmemory 2gb --maxmemory-policy allkeys-lru
volumes:
- ./docker/data/redis:/data
networks: [pimcore]
rabbitmq:
image: rabbitmq:3-management
container_name: pimcore_rabbitmq
ports:
- "5672:5672"
- "15672:15672"
environment:
- RABBITMQ_DEFAULT_USER=guest
- RABBITMQ_DEFAULT_PASS=guest
networks: [pimcore]
opensearch:
image: opensearchproject/opensearch:2.11.0
container_name: pimcore_opensearch
environment:
- discovery.type=single-node
- plugins.security.disabled=true
- OPENSEARCH_JAVA_OPTS=-Xms2g -Xmx2g
volumes:
- ./docker/data/opensearch:/usr/share/opensearch/data
ports:
- "9201:9200"
networks: [pimcore]
mercure:
image: dunglas/mercure:latest
container_name: pimcore_mercure
environment:
SERVER_NAME: ":80"
MERCURE_PUBLISHER_JWT_KEY: "YOUR_JWT_SECRET_KEY"
MERCURE_SUBSCRIBER_JWT_KEY: "YOUR_JWT_SECRET_KEY"
MERCURE_EXTRA_DIRECTIVES: |
anonymous
cors_origins *
networks: [pimcore]
networks:
pimcore:
Change YOUR_JWT_SECRET_KEY to a long random string — must match MERCURE_JWT_KEY in the app/.env.local.
docker/php/Dockerfile
FROM php:8.4-fpm
RUN apt-get update && apt-get install -y --no-install-recommends
git unzip libicu-dev libzip-dev libpng-dev libjpeg-dev libfreetype6-dev
libwebp-dev libonig-dev libxml2-dev libxslt1-dev libcurl4-openssl-dev
libpq-dev libssl-dev librabbitmq-dev graphviz supervisor
&& rm -rf /var/lib/apt/lists/*
RUN docker-php-ext-configure gd --with-jpeg --with-webp --with-freetype
&& docker-php-ext-install pdo pdo_mysql intl zip gd opcache bcmath soap xsl exif
RUN pecl install redis && docker-php-ext-enable redis
RUN pecl install amqp && docker-php-ext-enable amqp
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
COPY php.ini /usr/local/etc/php/php.ini
COPY php/pimcore-supervisor.conf /etc/supervisor/conf.d/pimcore.conf
WORKDIR /var/www/html
RUN printf '#!/bin/shnexec su -s /bin/sh www-data -c "php /var/www/html/bin/console $*"n'
> /usr/local/bin/pimcore && chmod +x /usr/local/bin/pimcore
CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisor/supervisord.conf"]
docker/php.ini
memory_limit=2G
max_execution_time=300
upload_max_filesize=512M
post_max_size=600M
opcache.enable=1
opcache.memory_consumption=512
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
realpath_cache_size=4096k
realpath_cache_ttl=600
Arrange opcache.validate_timestamps=1 in development so code changes take effect without restarting the container.
docker/php/pimcore-supervisor.conf
[program:php-fpm]
command=/usr/local/sbin/php-fpm --nodaemonize
autostart=true
autorestart=true
priority=1
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true
[program:messenger-consume]
command=php /var/www/html/bin/console messenger:consume
pimcore_generic_data_index_queue scheduler_generic_data_index
pimcore_core pimcore_maintenance pimcore_scheduled_tasks pimcore_image_optimize
--memory-limit=512M --time-limit=3600
numprocs=3
autostart=true
autorestart=true
user=www-data
priority=10
process_name=%(program_name)s_%(process_num)02d
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true
[program:consume-asset-update]
command=php /var/www/html/bin/console messenger:consume pimcore_asset_update
--memory-limit=512M --time-limit=3600
numprocs=1
autostart=true
autorestart=true
user=www-data
priority=10
process_name=%(program_name)s_%(process_num)02d
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true
[program:consume-execution-engine]
command=php /var/www/html/bin/console messenger:consume pimcore_generic_execution_engine
--memory-limit=512M --time-limit=3600
numprocs=1
autostart=true
autorestart=true
user=www-data
priority=10
process_name=%(program_name)s_%(process_num)02d
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true
[program:maintenance]
command=bash -c 'sleep 3600 && exec php /var/www/html/bin/console pimcore:maintenance'
autostart=true
autorestart=true
user=www-data
priority=10
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true
docker/nginx/default.conf
server {
listen 80;
server_name _;
return 301
}
server {
listen 443 ssl default_server;
server_name _;
ssl_certificate /etc/nginx/certs/pimcore.local.crt;
ssl_certificate_key /etc/nginx/certs/pimcore.local.key;
root /var/www/html/public;
index index.php;
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
gzip_min_length 1000;
gzip_comp_level 4;
gzip_vary on;
gzip_proxied any;
location /hub {
rewrite ^/hub(.*)$ /.well-known/mercure break;
proxy_pass
proxy_read_timeout 24h;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_buffering off;
proxy_cache off;
chunked_transfer_encoding on;
}
location = /admin { return 302 /pimcore-studio/login; }
location = /admin/ { return 302 /pimcore-studio/login; }
location ~ "image-thumb__[0-9]+__" {
try_files /var/tmp/thumbnails$uri /index.php$is_args$args;
expires max;
add_header Cache-Control "public, immutable";
log_not_found off;
}
location / {
try_files $uri /index.php$is_args$args;
}
location ~ ^/index.php(/|$) {
client_max_body_size 600m;
fastcgi_pass php:9000;
fastcgi_split_path_info ^(.+.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTPS on;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_read_timeout 300;
}
location ~ .php$ { return 404; }
location ~* .(jpg|jpeg|png|gif|webp|svg|css|js|ico|xml)$ {
try_files $uri /index.php$is_args$args;
expires max;
log_not_found off;
}
}
application/.env
Commit to safe placeholder values:
APP_ENV=dev
APP_DEBUG=true
APPLICATION_SECRET=CHANGE_ME_APPLICATION_SECRET_KEY_LONG_ENOUGH_FOR_VALIDATION
DATABASE_URL=mysql://pimcore:pimcore@db:3306/pimcore
PIMCORE_ADMIN_USER=admin
PIMCORE_ADMIN_PASSWORD=admin
PIMCORE_OPENSEARCH_DSN=opensearch://opensearch:9200
PIMCORE_MESSENGER_TRANSPORT_DSN_PREFIX=amqp://guest:guest@rabbitmq:5672/%2f/
RABBITMQ_DSN=amqp://guest:guest@rabbitmq:5672/%2f
MERCURE_JWT_KEY=CHANGE_ME_THIS_IS_MY_SECRET_KEY_THAT_IS_LONG_ENOUGH_FOR_VALIDATION
MERCURE_URL=
MERCURE_SERVER_URL=
app/.env.local
Created only on the server — never committed:
APP_ENV=prod
APP_DEBUG=false
# Generate with: openssl rand -hex 32
APPLICATION_SECRET=<your-random-secret>
PIMCORE_BASE_URL=
# Must match MERCURE_PUBLISHER_JWT_KEY in docker-compose.yml
MERCURE_JWT_KEY=<same-key-as-docker-compose>
New Instance Setup
1. Clone the repository
git clone <repo-url> pimcore-docker
cd pimcore-docker
2. Create application/.env.local
Copy the template above and fill in the actual values.
3. Generate an SSL certificate
mkdir -p docker/nginx/certs
openssl req -x509 -nodes -days 3650 -newkey rsa:2048
-keyout docker/nginx/certs/pimcore.local.key
-out docker/nginx/certs/pimcore.local.crt
-subj "/CN=pimcore.local"
For the original domain, place your CA signature .crt And .key in the docker/nginx/certs/ and update default.conf.
4. Build and start the container
docker compose up -d --build
Wait ~30 seconds, then verify all services are displayed Up:
docker compose ps
5. Install Composer dependencies
docker exec pimcore_php composer install --no-dev --optimize-autoloader --no-interaction -d /var/www/html
6. Install Pimcore (first run only)
docker exec pimcore_php pimcore pimcore:install
--admin-username=admin --admin-password=admin
--mysql-host-socket=db --mysql-database=pimcore
--mysql-username=pimcore --mysql-password=pimcore
--no-interaction
On the next deployment, run the migration:
docker exec pimcore_php pimcore doctrine:migrations:migrate --no-interaction
7. Install assets, warm cache, build index
docker exec -u www-data pimcore_php php /var/www/html/bin/console assets:install --symlink
docker exec -u www-data pimcore_php php /var/www/html/bin/console cache:warmup --env=prod
docker exec -u www-data pimcore_php php /var/www/html/bin/console generic-data-index:update-index-settings --all
8. Verification
— Pimcore admin login— RabbitMQ UI (guest/guest)— OpenSearch REST API
General Orders
# Logs
docker compose logs -f
docker compose logs -f php
# Console commands (as www-data)
docker exec pimcore_php pimcore <command>
# Shell access
docker exec -it pimcore_php bash
# Clear cache
docker exec -u www-data pimcore_php php /var/www/html/bin/console cache:clear
# Restart workers after a code change
docker compose restart php
# Reload Nginx without downtime
docker exec pimcore_nginx nginx -s reload
# Rebuild PHP image after Dockerfile changes
docker compose up -d --build php
# Stop everything
docker compose down
# Wipe all data (destructive)
docker compose down && rm -rf docker/data/mysql docker/data/opensearch docker/data/redis
Solution to problem
The container will not start — check logs:
docker compose up --build
docker compose logs php
502 Bad Gate — PHP may still be in the initialization process. Check supervisor:
docker exec pimcore_php supervisorctl status
All programs should be displayed RUNNING. If php-fpm show FATAL:
docker exec pimcore_php supervisorctl tail php-fpm
Database rejected — MySQL takes 20–30 seconds on first boot. Watch until “ready for connection”:
docker compose logs -f db
Permission error on var/:
docker exec pimcore_php bash -c
"chown -R www-data:www-data /var/www/html/var /var/www/html/public/var &&
chmod -R 775 /var/www/html/var /var/www/html/public/var"
The OpenSearch index is empty after import — check workers and queues:
docker exec pimcore_php supervisorctl status messenger-consume:*
docker exec pimcore_php pimcore messenger:stats
That ./app directory already mounted in bind — PHP file edits will take effect immediately. Dockerfile or supervisor configuration changes are required docker compose up -d --build php.
PakarPBN
A Private Blog Network (PBN) is a collection of websites that are controlled by a single individual or organization and used primarily to build backlinks to a “money site” in order to influence its ranking in search engines such as Google. The core idea behind a PBN is based on the importance of backlinks in Google’s ranking algorithm. Since Google views backlinks as signals of authority and trust, some website owners attempt to artificially create these signals through a controlled network of sites.
In a typical PBN setup, the owner acquires expired or aged domains that already have existing authority, backlinks, and history. These domains are rebuilt with new content and hosted separately, often using different IP addresses, hosting providers, themes, and ownership details to make them appear unrelated. Within the content published on these sites, links are strategically placed that point to the main website the owner wants to rank higher. By doing this, the owner attempts to pass link equity (also known as “link juice”) from the PBN sites to the target website.
The purpose of a PBN is to give the impression that the target website is naturally earning links from multiple independent sources. If done effectively, this can temporarily improve keyword rankings, increase organic visibility, and drive more traffic from search results.