remix icon indicating copy to clipboard operation
remix copied to clipboard

Development inside of Docker

Open Tjerk-Haaye-Henricus opened this issue 2 years ago • 5 comments

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.

Tjerk-Haaye-Henricus avatar Aug 22 '23 08:08 Tjerk-Haaye-Henricus

Same issue for me too

Julious1994 avatar Aug 22 '23 11:08 Julious1994

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...

ngbrown avatar Aug 22 '23 18:08 ngbrown

Wow thats really cool, let me have a trie on my end. Thanks for that information.

Tjerk-Haaye-Henricus avatar Aug 27 '23 17:08 Tjerk-Haaye-Henricus

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

BojanHribernik avatar Feb 27 '24 12:02 BojanHribernik

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

I’m using this configuration with some adjustments. Checking it out now at https://github.com/ramaID/minimal-remix/commit/600ee8c75290fa881f05bedeb2506cad6188e8ed

ramaID avatar Aug 06 '24 02:08 ramaID