Shopify app dev from Docker on Linux

Hello!

I have a Docker file with Shopify app and globally installed NPM Shopify CLI package version 3.76.2:

FROM php:8.2-fpm

RUN apt-get update

# Install required extensions
RUN apt-get install -y \
    procps \
    xdg-utils \
    curl \
    git \
    libicu-dev \
    libzip-dev \
    libmcrypt-dev \
    libpq-dev \
    libjpeg-dev \
    libpng-dev \
    libgd-dev

# Install PHP extensions
RUN docker-php-ext-install \
    intl \
    zip \
    pdo_pgsql \
    gd \
    bcmath \
    pdo_mysql

# Install PHP PECL extensions
RUN pecl install mcrypt-1.0.6 && docker-php-ext-enable mcrypt \
    && pecl install xdebug-3.3.1 && docker-php-ext-enable xdebug

ARG USER_ID=1000
ARG GROUP_ID=1000

# Create user inside dockerc
RUN groupadd -g ${GROUP_ID} _user && useradd -u ${USER_ID} -g _user -m _user
RUN chown -R _user:_user /var/www/html

# Install Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

USER _user

ARG NODE_VERSION=20.17.0

# Install Node.js v15
ENV NODE_VERSION=${NODE_VERSION}
ENV NVM_DIR=/home/_user/.nvm
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash \
    && . ${NVM_DIR}/nvm.sh \
    && nvm install ${NODE_VERSION} \
    && nvm alias default ${NODE_VERSION} \
    && nvm use default \
    && echo 'export NVM_DIR="$HOME/.nvm"; [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm' >> ~/.bashrc
ENV PATH="${NVM_DIR}/versions/node/v${NODE_VERSION}/bin:${PATH}"

ARG SHOPIFY_CLI_VERSION=3.75.0
RUN npm install -g \
    @shopify/cli@${SHOPIFY_CLI_VERSION} \
    @shopify/plugin-cloudflare@${SHOPIFY_CLI_VERSION}
ENV CODESPACES=1

# Set working directory
WORKDIR /var/www/html

# Expose ports 9000, 9003, 3457
EXPOSE 9000
EXPOSE 9003
EXPOSE 3457

# Start PHP-FPM
CMD ["php-fpm", "—nodaemonize"]

I use it to build the app service in the Docker Compose project. Here is the relevant section of the docker-compose.yml file:

services:
  app:
    build:
      context: ./.docker/php
      dockerfile: Dockerfile
      args:
        NODE_VERSION: 20.17.0
        SHOPIFY_CLI_VERSION: 3.76.2
    volumes:
      - ./:/var/www/html
      - ./.docker/php/conf.d/xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
      - ./.docker/php/php.ini-development:/usr/local/etc/php/php.ini
    ports:
      - "9003:9003"
      - "${SHOPIFY_GRAPHQL_PORT:-3457}:3457"
    networks:
      - default
    privileged: true
    extra_hosts:
      - host.docker.internal:host-gateway

Inside the app service container, I call the command to debug the Shopify app:

shopify app dev

This command should open the Cloudflare tunnel and this works fine for macOS and Windows, but on Linux the command results in an error:

With --verbose (shopify app dev --verbose):

2025-03-12T10:51:41.435Z: Random port obtained: 59803
2025-03-12T10:51:41.937Z: Polling tunnel status for cloudflare (attempt 1): starting
2025-03-12T10:51:42.438Z: Polling tunnel status for cloudflare (attempt 2): starting
2025-03-12T10:51:42.939Z: Polling tunnel status for cloudflare (attempt 3): starting
...
2025-03-12T10:51:55.733Z: failed to request quick Tunnel: Post "https://api.trycloudflare.com/tunnel": context deadline exceeded (Client.Timeout exceeded while awaiting headers)

2025-03-12T10:51:55.736Z: Cloudflare tunnel crashed: Command failed with exit code 1: /home/_user/.nvm/versions/node/v20.17.0/lib/node_modules/@shopify/cli/bin/cloudflared tunnel --url http://localhost:55421 --no-autoupdate
2025-03-12T10:51:40Z INF Thank you for trying Cloudflare Tunnel. Doing so, without a Cloudflare account, is a quick way to experiment and try it out. However, be aware that these account-less Tunnels have no uptime guarantee. If you intend to use Tunnels in production you should use a pre-created named tunnel by following: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps
2025-03-12T10:51:40Z INF Requesting new quick Tunnel on trycloudflare.com...
failed to request quick Tunnel: Post "https://api.trycloudflare.com/tunnel": context deadline exceeded (Client.Timeout exceeded while awaiting headers), restarting...
...
2025-03-12T10:52:20.048Z: Polling tunnel status for cloudflare (attempt 77): starting
2025-03-12T10:52:20.550Z: Polling tunnel status for cloudflare (attempt 78): starting
2025-03-12T10:52:20.721Z: Killing process 406: /home/_user/.nvm/versions/node/v20.17.0/lib/node_modules/@shopify/cli/bin/cloudflared tunnel --url http://localhost:55421 --no-autoupdate
2025-03-12T10:52:21.051Z: Polling tunnel status for cloudflare (attempt 79): error

I guessed that it might be about using AppArmor on a Linux host. So I did the following:

  1. Created a shopify-cli-cloudflare-profile using ChatGPT (I’m not sure of its contents):
#include <tunables/global>

profile shopify-cli-cloudflare-profile flags=(attach_disconnected,mediate_deleted) {
    #include <abstractions/base>

    # Allow reading and writing to /tmp
    /tmp/** rw,
    # Allow access to files in /var/www/html (if necessary)
    /var/www/html/** rw,
    # Allow access to device /dev/null
    /dev/null rw,

    # Block access to critical files
    deny /etc/shadow r,
    deny /root/** rwk,
}
  1. Loaded the AppArmor profile:
sudo apparmor_parser -r shopify-cli-cloudflare-profile
  1. Modified docker-compose.yml as follows:
services:
  app:
    build:
      context: ./.docker/php
      dockerfile: Dockerfile
      args:
        NODE_VERSION: 20.17.0
        SHOPIFY_CLI_VERSION: 3.76.2
    volumes:
      - ./:/var/www/html
      - ./.docker/php/conf.d/xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
      - ./.docker/php/php.ini-development:/usr/local/etc/php/php.ini
    network_mode: host
    security_opt:
      - apparmor=shopify-cli-cloudflare-profile
    privileged: true
    extra_hosts:
      - host.docker.internal:host-gateway
  1. I am using Docker Desktop, so I enabled the Enable host networking option in Settings → Resources → Network.

But this approach also failed. Cloudflare tunnel from Docker container on Linux host cannot be opened. Could you please tell me what could be the problem?

Hey Vladyslav! Sorry to hear about this issue. It appears to be a network issue with cloudflare when the CLI tries to create a quick tunnel. These quick tunnels do not guarantee any SLA or uptime [0] and there have been similar incidents for them in the past [1]. You may try just trying again. Alternatively, rather than having the CLI setup this quick tunnel, you can supply your own tunnel to the dev command using the --tunnel-url flag.

Hi @craigmartin

Thank you for your reply. However, the links provided don’t answer the question of why this works on macOS and Windows and doesn’t work Linux. I think it’s a matter of specific implementation of networking in Docker for Linux or a firewall or something similar.