PHP 8.1.4 Fpm Alpine - install NPM as none root? causing issues

Hi All

I was wondering if someone could please help. Ive been struggling a while to run nginx and php in a container with npm.

Php and nginx are run by supervisor and this all works fine. The issue comes in when i change the folder owners of my project and then run npm ci and npm run prod for laravel.

I get the following issue:

#PHP
FROM php:8.1.4-fpm-alpine AS digital_dealer_php

ARG APP_ENV
ENV APP_HOME /var/www/html
ARG UID=1000
ARG GID=1000
ENV USERNAME=root

ENV TZ=UTC

WORKDIR $APP_HOME

RUN apk add --update bash zip unzip curl sqlite nginx supervisor php8 \
    php8-common \
    php8-fpm \
    php8-pdo \
    php8-opcache \
    php8-zip \
    php8-phar \
    php8-iconv \
    php8-cli \
    php8-curl \
    php8-openssl \
    php8-mbstring \
    php8-tokenizer \
    php8-fileinfo \
    php8-json \
    php8-xml \
    php8-xmlwriter \
    php8-simplexml \
    php8-dom \
    php8-pdo_mysql \
    php8-pdo_sqlite \
    php8-tokenizer \
    php8-pecl-redis \
    php8-xdebug \
    npm

RUN npm install -g npm
RUN echo http://dl-2.alpinelinux.org/alpine/edge/community/ >> /etc/apk/repositories
RUN apk --no-cache add shadow && usermod -u 1000 www-data

RUN docker-php-ext-install mysqli pdo pdo_mysql

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

# Add user for laravel application
#RUN addgroup -g ${GID} ${USERNAME}
#RUN adduser -D -s /bin/bash -G ${USERNAME} -u 1337 ${USERNAME}
#RUN usermod -a -G ${USERNAME} nginx


# Copy existing application directory contents
#COPY . $APP_HOME
COPY --chown=${USERNAME}:${USERNAME} . $APP_HOME

#Copy supervisor to manage nginx and php processes
COPY ./docker/php-fpm/supervisord.conf /etc/
#Nginx config
COPY ./docker/nginx/default.conf /etc/nginx/http.d/default.conf
#PHP ini config
COPY ./docker/php-fpm/php.ini /usr/local/etc/php/php.ini

# Docker entrypoint script
COPY ./docker/php-fpm/docker-entrypoint.sh /usr/local/bin/start-container
RUN chmod +x /usr/local/bin/start-container

#USER sail

EXPOSE 80
ENTRYPOINT ["start-container"]

#Prod
FROM digital_dealer_php AS digital_dealer_php_prod
RUN composer install --ignore-platform-reqs --optimize-autoloader --no-dev
RUN echo "building Prod with composer and npm"
#COPY --chown=${USERNAME}:${USERNAME} .env.production $APP_HOME/.env
COPY .env.production $APP_HOME/.env
#RUN chown -R root /var/www/html
RUN php artisan optimize:clear
RUN php artisan storage:link
RUN npm ci
RUN npm run prod

If i change my project back to root using RUN chown -R root /var/www/html then npm works again and has access to installing and placing files where it needs to.

I would like to run the container non root however for security.

Thanks

First of all, you should not install php packages the way you did in an official php image. Those official images build PHP from source code. If you install PHP from the alpine repository, it either won’t affect the original PHP in the image or you downgrade it since PHP 8 from the repository is older than the built version. Using docker-php-ext-install is okay. Sometimes you need docker-php-ext-configure too to build from source code.

I don’t think you should change the id of www-data. Since it is PHP FPM, you can tell FPM to run as an other user. You could change www-data to an other user with that ID or you just use the ID directly.

This is very strange, because if it works only when the owner of the files is root, it means the user NPM is running on behalf is chosen based on the owner of a file in that folder. If it were running as root, it would not be affected by any ownership since the root user has access to everything in the container. You can also see in the error messages that it cannot write the logs either in /root/.npm/_logs as if it wasn’t running as root.

I can see you try to set the UID in the first stage as an environment variable. I don’t know if that works, but that variable is readonly in a shell and not without reason. So maybe you should change that too and use USER_ID and GROUP_ID instead when necessary

Thanks for your reply.

I did not realise that everything i was installing , the image already has installed. I have removed all the php installs and only have this now

RUN apk add --update bash zip unzip curl sqlite supervisor npm

i removed nginx now from being in the same container as php and split it out into its own container.
I have nginx container and php container which seems to work better for me. I think this is the right choice as i ran into too many issues with them being combined.

1 Like

Exactly. I didn’t want to mention that because I thought you had good reason to do this way, but usually it’s much better to use separate containers for every service.

Have you solved the original permission issue then?