remix
                                
                                 remix copied to clipboard
                                
                                    remix copied to clipboard
                            
                            
                            
                        Development inside of Docker
What version of Remix are you using?
1.19.3
Are all your remix dependencies & dev-dependencies using the same version?
- [X] Yes
Steps to Reproduce
Hey there,
first of all i'm really into remix. The lack of client side state management sucks a bit when you come from application development but yeah shit happens :D
But now the Issue
I'm a friend of keeping things in one environment from development to production. So i now exaclty that things work on all steps of my delivering pipeline. So most of the time therefore docker is my best friend.
Exept when it comes to remix. I tried a lot of ways to manage a good development process inside of docker but didn't had a good fit until yet. e.g. Next js is super easy when it comes to that.
So what are the plans of supporting docker in development. especially when it comes to hot reloading etc.
Am i the only one doing this, or do i have company here :D
Expected Behavior
HMR and also Hot reloading should work. There should be a documented way how to develop in Docker.
Actual Behavior
I can build and run inside of docker but a soon as i want to have all the dev features i'm quite lost.
Same issue for me too
I do my Remix development in Docker because native in Windows had slow re-build times (because of Anti-Virus, etc.) and because the rest of the services of the application are also containers. Using WSL 2, using a mount from docker to the Windows filesystem isn't an option because file update notifications don't work. Likewise, opening the project from the Linux file system would have excess overhead because of the IDE reading through all the files in node_modules.
My approach has the Remix dev server, Live Reload, HMR, HDR, and Node.js debugging all working. It also separates the node_modules between the Docker environment and Windows, useful for the case of native Node.js modules.
I have a docker-compose.yml (and overrides) to spin up the Remix container to a "dev" target and a atmoz/sftp container with a shared volume between the two.
The "dev" target in Dockerfile:
FROM deps as dev
ENV NODE_ENV development
RUN apt-get update \
    && apt-get install -y \
    rsync procps \
    && rm -rf /var/lib/apt/lists/*
RUN mkdir /home/node/app-dev \
    && chmod -R +w /home/node/app-dev \
    && chown node /home/node/app-dev
COPY . .
RUN date --iso-8601=seconds --utc > /home/node/app/.rsync-ref
USER node
I sync the contents of the volume at startup by running this command:
    command:
        - sh
        - -c
        - |-
          TERM=linux clear \
          && if cmp -s /home/node/app/.rsync-ref /home/node/app-dev/.rsync-ref; \
             then echo "Skipping rsync..."; \
             else rsync -rlptOEihW --no-compress --delete --exclude='node_modules/' --exclude='.cache/' --exclude='build/' --exclude='public/build/' /home/node/app/ /home/node/app-dev/; \
             fi \
          && npm install --no-progress -d --no-fund --no-audit \
          && exec ./node_modules/.bin/remix dev --manual -c "node --inspect=0.0.0.0:9229 ./server.js"
WebStorm can then sync via sftp on every file save. There's probably extensions for the same with Visual Studio Code.
Entire Remix Dockerfile: (expand to view)
FROM node:18-bookworm-slim as base
# set for base and all layer that inherit from it
ENV NODE_ENV production
# disable npm update check and notification
RUN npm --no-update-notifier config set --global update-notifier false
# Create app directory
WORKDIR /home/node/app
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./
COPY ./patches ./patches
# Install all node_modules, including dev dependencies
FROM base as deps
RUN npm ci --include=dev
FROM deps as dev
ENV NODE_ENV development
RUN apt-get update \
    && apt-get install -y \
    rsync procps \
    && rm -rf /var/lib/apt/lists/*
RUN mkdir /home/node/app-dev \
    && chmod -R +w /home/node/app-dev \
    && chown node /home/node/app-dev
COPY . .
RUN date --iso-8601=seconds --utc > /home/node/app/.rsync-ref
USER node
# Setup production node_modules
FROM deps as production-deps
RUN npm prune --omit=dev
# Build the app
FROM deps as build
COPY . .
RUN npm run build
# Finally, build the production image with minimal footprint
FROM base
COPY --from=production-deps /home/node/app/node_modules ./node_modules
# Bundle app source
COPY server.js ./
COPY --from=build /home/node/app/build ./build
COPY --from=build /home/node/app/public ./public
USER node
EXPOSE 3000
CMD [ "node", "./server.js" ]
Relevant portions of docker-compose.yml merged with the dev override files: (expand to view)
services:
  web-app-sftp:
    image: atmoz/sftp:debian
    volumes:
      - web-app-dev:/home/node/app-dev
    ports:
      - "3022:22"
    command: node:pass:1000
  web-app:
    image: ${DOCKER_REGISTRY-}web-app-dev
    build:
      context: web-app
      dockerfile: Dockerfile
      target: dev
    depends_on:
      web-app-sftp:
        condition: service_started
    working_dir: /home/node/app-dev
    ports:
      - "3000:3000"
      - "9229:9229"
      - "8002:8002"
    volumes:
      - web-app-dev:/home/node/app-dev
    command:
        - sh
        - -c
        - |-
          TERM=linux clear \
          && if cmp -s /home/node/app/.rsync-ref /home/node/app-dev/.rsync-ref; \
             then echo "Skipping rsync..."; \
             else rsync -rlptOEihW --no-compress --delete --exclude='node_modules/' --exclude='.cache/' --exclude='build/' --exclude='public/build/' /home/node/app/ /home/node/app-dev/; \
             fi \
          && npm install --no-progress -d --no-fund --no-audit \
          && exec ./node_modules/.bin/remix dev --manual -c "node --inspect=0.0.0.0:9229 ./server.js"
volumes:
  web-app-dev:
This is what works for me. It would be useful to have an official recommendation to start from, but with overrides and different IDE's, there's endless variations...
Wow thats really cool, let me have a trie on my end. Thanks for that information.
Remix + Vite + Docker
One of the projects I am working on, requires me to use Remix inside Docker container in development. This is my setup:
docker-compose.yml
services:
  app:
    user: 1000:1000
    build: 
      context: ./src
      dockerfile: Dockerfile.local
    volumes:
      - ./src:/opt/app:rw
    ports:
      - 3000:3000 # remix
      - 3010:3010 # vite hmr
Dockerfile.local
FROM node:20.10-slim as base
WORKDIR /opt/app
EXPOSE 3000
# keep the container running
CMD sleep infinity
vite.config.ts
import { unstable_vitePlugin as remix } from '@remix-run/dev'
import { defineConfig } from 'vite'
import tsconfigPaths from 'vite-tsconfig-paths'
import remixConfig from './remix.config'
export default defineConfig({
  plugins: [remix(remixConfig), tsconfigPaths()],
  server: {
    hmr: {
      port: 3010,
    }
  }
})
/** @type {import('@remix-run/dev').AppConfig} */
export default {
  ignoredRouteFiles: ['**/.*', '**/*.test.{js,jsx,ts,tsx}', '**/__*.*'],
  serverModuleFormat: 'esm',
  future: {
    v3_relativeSplatPaths: true,
  },
}
# start container
docker compose up -d
# enter the container
docker compose exec app bash
# inside the container
npm run dev
NOTE: I literally just got it working, so there might be some issues, which I haven't encountered yet ;)
Remix Version: 2.5.1
Remix + Vite + Docker
One of the projects I am working on, requires me to use Remix inside Docker container in development. This is my setup:
docker-compose.yml
services: app: user: 1000:1000 build: context: ./src dockerfile: Dockerfile.local volumes: - ./src:/opt/app:rw ports: - 3000:3000 # remix - 3010:3010 # vite hmrDockerfile.local
FROM node:20.10-slim as base WORKDIR /opt/app EXPOSE 3000 # keep the container running CMD sleep infinityvite.config.ts
import { unstable_vitePlugin as remix } from '@remix-run/dev' import { defineConfig } from 'vite' import tsconfigPaths from 'vite-tsconfig-paths' import remixConfig from './remix.config' export default defineConfig({ plugins: [remix(remixConfig), tsconfigPaths()], server: { hmr: { port: 3010, } } })/** @type {import('@remix-run/dev').AppConfig} */ export default { ignoredRouteFiles: ['**/.*', '**/*.test.{js,jsx,ts,tsx}', '**/__*.*'], serverModuleFormat: 'esm', future: { v3_relativeSplatPaths: true, }, }# start container docker compose up -d # enter the container docker compose exec app bash # inside the container npm run devNOTE: I literally just got it working, so there might be some issues, which I haven't encountered yet ;)
Remix Version: 2.5.1
I’m using this configuration with some adjustments. Checking it out now at https://github.com/ramaID/minimal-remix/commit/600ee8c75290fa881f05bedeb2506cad6188e8ed