When working with Symfony applications in Docker containers, a particularly puzzling issue occurs where chmod
commands in the Dockerfile appear to set permissions correctly (as verified by ls -a
), yet Apache still throws permission denied errors. Only when running the same chmod
commands manually inside the container does the issue resolve.
The core issue stems from how Docker handles volume permissions during the build phase versus runtime. The permissions set during build time don't properly account for:
- The UID/GID mapping between host and container
- Apache's default user (www-data) permissions
- Docker's layer caching behavior
Here are several approaches that actually work:
1. Using the Correct User During Build
# Dockerfile solution
FROM php:7-apache
# Set the correct user before permission changes
ARG userid=33
ARG groupid=33
RUN usermod -u ${userid} www-data && \
groupmod -g ${groupid} www-data
# ... rest of your Dockerfile ...
# Run chmod as www-data user
USER www-data
RUN find /var/www/html/ -type d -exec chmod 755 {} \; && \
find /var/www/html/ -type f -exec chmod 644 {} \; && \
chmod -R 777 /var/www/html/app/cache /var/www/html/app/logs
USER root
2. Entrypoint Script Solution
Create an entrypoint.sh:
#!/bin/bash
set -e
# Fix permissions
chown -R www-data:www-data /var/www/html
chmod -R 755 /var/www/html
chmod -R 777 /var/www/html/app/cache
chmod -R 777 /var/www/html/app/logs
# Start Apache
exec apache2-foreground
Then update your Dockerfile:
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
3. Docker Compose Solution
If using Docker Compose, you can set user permissions at runtime:
version: '3'
services:
app:
build: .
user: "33:33"
volumes:
- ./:/var/www/html
- Always set permissions as late as possible in your container lifecycle
- Consider using named volumes instead of host mounts for production
- For development, the entrypoint script approach is most reliable
- Test permissions thoroughly before deploying to production
Many developers encounter this puzzling scenario where chmod
commands in Dockerfiles appear to execute successfully, yet the containerized application (especially web servers like Apache) still throws permission errors. What makes this particularly frustrating is that manually running the same chmod
commands inside the container fixes the issue immediately.
The core issue lies in how Docker handles volume permissions during the build process versus runtime. When you run chmod
in the Dockerfile, it affects the base image layers, but these permissions can be overridden when:
- Volume mounts are used (even anonymous ones)
- The host filesystem permissions interfere
- User namespace remapping is enabled
- The container runs with different UID/GID than during build
Here's an improved Dockerfile approach that addresses the permission issues:
FROM php:7-apache
# Set the user/group explicitly
ARG USER_ID=33
ARG GROUP_ID=33
RUN apt-get update && \\
apt-get install -y libicu-dev freetds-common freetds-bin unixodbc && \\
docker-php-ext-install intl mbstring && \\
a2enmod rewrite
# Create the www-data user with specific UID/GID to match host
RUN usermod -u ${USER_ID} www-data && \\
groupmod -g ${GROUP_ID} www-data
COPY app/php.ini /usr/local/etc/php/
COPY app/apache2.conf /etc/apache2/apache2.conf
# Copy files with correct ownership
COPY --chown=www-data:www-data ./ /var/www/html
# Set permissions as www-data user
USER www-data
RUN find /var/www/html/ -type d -exec chmod 755 {} \\; && \\
find /var/www/html/ -type f -exec chmod 644 {} \\; && \\
chmod -R 777 /var/www/html/app/cache /var/www/html/app/logs
# Switch back to root for Apache to run properly
USER root
If the above solution doesn't fully resolve your issue, consider these additional methods:
1. Entrypoint Script Solution
#!/bin/bash
chown -R www-data:www-data /var/www/html/app/cache
chown -R www-data:www-data /var/www/html/app/logs
exec apache2-foreground
Add to your Dockerfile:
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
ENTRYPOINT ["docker-entrypoint.sh"]
2. Using the correct build context
Ensure your build context includes all necessary files with proper permissions on the host first:
chmod -R 755 ./app/cache
chmod -R 755 ./app/logs
docker build -t myapp .
When troubleshooting, these commands are invaluable:
# Check current user in container
docker exec -it container_id whoami
# Inspect file permissions
docker exec -it container_id ls -la /var/www/html/app/cache
# Check process ownership
docker exec -it container_id ps aux