phplaraveldockerlaravel-artisantinker

Docker Container Define user home folder


I have the following php service in docker-compse.yml

version: '3'

networks:
  laravel:
    driver: bridge

services:
  nginx:
    image: nginx:stable-alpine
    restart: unless-stopped
    ports:
      - "${WEB_PORT}:80"
    volumes:
      - "${PROJECT_DIR}:/var/www/html"
      - "${NGINX_CONFIG}:/etc/nginx/conf.d/default.conf"
      - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - php
      - mysql
    networks:
      - laravel

  mysql:
    image: mysql:5.7.29
    restart: unless-stopped
    user: "${HOST_UID}:${HOST_GID}"
    tty: true
    ports:
      - "${SQL_PORT}:3306"
    environment:
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      SERVICE_TAGS: dev
      SERVICE_NAME: mysql
    volumes:
      - ./docker/mysql:/var/lib/mysql
    networks:
      - laravel

  php:
    build:
      context: ./docker
      dockerfile: Dockerfile-php
    user: "${HOST_UID}:${HOST_GID}"
    volumes:
      - "${PROJECT_DIR}:/var/www/html"
      - ./docker/php/php.ini:/usr/local/etc/php/php.ini
      #- "${COMPOSER_CACHE_DIR}:/.composer/cache"
      #- "${COMPOSER_CONFIG}:/.composer/config"
    working_dir: /var/www/html
    networks:
      - laravel

  npm:
    image: node:13.7
    user: "${HOST_UID}:${HOST_GID}"
    volumes:
      - "${PROJECT_DIR}:/var/www/html"
    working_dir: /var/www/html
    entrypoint: ['npm']

When I run whoami in the container, it returns:

whoami: cannot find name for user ID 1000

I think this is a problem because there is no home directory, docker-compose exec php ls ~ returns:

ls: cannot access '/home/clarg': No such file or directory

This then leads to docker-compose exec php php artisan tinker returning:

   ErrorException 

  Writing to directory /.config/psysh is not allowed.

  at vendor/psy/psysh/src/ConfigPaths.php:362
    358▕             @\mkdir($dir, 0700, true);
    359▕         }
    360▕ 
    361▕         if (!\is_dir($dir) || !\is_writable($dir)) {
  ➜ 362▕             \trigger_error(\sprintf('Writing to directory %s is not allowed.', $dir), \E_USER_NOTICE);
    363▕ 
    364▕             return false;
    365▕         }
    366▕ 

      +20 vendor frames 
  21  artisan:37
      Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))

From googling, I see this is in the home directory, which does not exist in the container.

How can I solve this?

EDIT:

Dockerfile-php:

FROM php:8.0-fpm

ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/

RUN chmod +x /usr/local/bin/install-php-extensions && \
    install-php-extensions gd zip pdo_mysql

# check https://github.com/mlocati/docker-php-extension-installer#supported-php-extensions for more extensions

COPY --from=composer /usr/bin/composer /usr/bin/composer

php.ini https://pastebin.com/T2iYTZz2


Solution

  • Your docker containers don't have any knowledge of the users that may or may not exist on the host machine, so unless you've built those in with their accompanying config and directory structure the only thing you're getting out of feeding docker your local UID and GID is "running the container as something other than root", which is good.

    But generally you don't want to tie a docker container/image to the particular environment that it is launched from, eg: requiring a user with the same name as your local user exist within the container, plus all of its associated directories and such.

    In this specific case it looks like artisan just wants to cache some config, and you can control where that lands with the environment variable:

    XDG_CONFIG_HOME=/some/writeable/directory
    

    Which you could set in the Dockerfile, docker-compose, or .env file of your project. I would suggest setting it to somewhere in your project directory, but outside of the docroot.

    Ref: https://stackoverflow.com/a/62041096/1064767

    This is well enough for local dev where you want to mount in your local work dir for testing, but will likely need a bit more consideration if you're going to build/deploy a final docker image.