4

With the help from SO community I was finally able to dockerize my Sveltekit app and access it from the browser (this was an issue initially). So far so good, but now every time I perform a code change I need to re-build and redeploy my container which obviously is not acceptable. Hot reload is not working, I've been trying multiple things I've found online but none of them have worked so far.

Here's my Dockerfile:

FROM node:19-alpine

# Set the Node environment to development to ensure all packages are installed
ENV NODE_ENV development

# Change our current working directory
WORKDIR /app

# Copy over `package.json` and lock files to optimize the build process
COPY  package.json package-lock.json ./
# Install Node modules
RUN npm install

# Copy over rest of the project files
COPY . .

# Perhaps we need to build it for production, but apparently is not needed to run dev script.
# RUN npm run build

# Expose port 3000 for the SvelteKit app and 24678 for Vite's HMR
EXPOSE 3333
EXPOSE 8080
EXPOSE 24678

CMD ["npm", "run", "dev"]

My docker-compose:

version: "3.9"

services:
  dmc-web:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: dmc-web
    restart: always
    ports:
      - "3000:3000"
      - "3010:3010"
      - "8080:8080"
      - "5050:5050"
      - "24678:24678"
    volumes:
      - ./:/var/www/html

the scripts from my package.json:

    "scripts": {
        "dev": "vite dev --host 0.0.0.0",
        "build": "vite build",
        "preview": "vite preview",
        "test": "playwright test",
        "lint": "prettier --check . && eslint .",
        "format": "prettier --write ."
    },

and my vite.config.js:

import { sveltekit } from '@sveltejs/kit/vite';
import {defineConfig} from "vite";

export default defineConfig({
    plugins: [sveltekit()],
    server: {
        watch: {
            usePolling: true,
        },
        host: true, // needed for the DC port mapping to work
        strictPort: true,
        port: 8080,
    }
});

any idea what am I missing? I can reach my app at http://localhost:8080 but cannot get to reload the app when a code change happens.

Thanks.

MrCujo
  • 1,218
  • 3
  • 31
  • 56

2 Answers2

5

Solution

The workspace in question does not work simply because it does not bind-mount the source directory. Other than that, it has no problem whatsoever.

Here's working code at my github: https://github.com/rabelais88/stackoverflow-answers/tree/main/74680419-svelte-docker-HMR

1. Proper bind mount in docker-compose

The docker-compose.yaml from the question only mounts the result of previous build, not the current source files.

# wrong
    volumes:
      - ./:/var/www/html
# ✅answer
    volumes:
      # it avoids mounting the workspace root
      # because it may cause OS specific node_modules folder
      # or build folder(.svelte-kit) to be mounted.
      # they conflict with the temporary results from docker space.
      # this is why many mono repos utilize ./src folder
      - ./src:/home/node/app/src
      - ./static:/home/node/app/static
      - ./vite.config.js:/home/node/app/vite.config.js
      - ./tsconfig.json:/home/node/app/tsconfig.json
      - ./svelte.config.js:/home/node/app/svelte.config.js

2. dockerfile should not include file copy and command

dockerfile does not always have to include command. it is necessary when 1)the result has to be preserved 2)the process lifecycle is critical to image. In this case 1)the result is not quite certain because the source may not be complete at the moment of booting, 2) the process lifecycle is not really important because the user may manually execute or close the container. The local development environment for VSCode + Docker, a.k.a VSCode devcontainer, also uses sleep infinity command for this reason.

As mentioned above, the code cannot be copied to docker space because it would conflict with bind-mounted files. To avoid both files collide, just remove COPY and CMD command from dockerfile and add more commands at docker-compose.yaml

# dockerfile

# wrong
COPY  package.json package-lock.json ./
RUN npm install
COPY . .
# ...
CMD ["npm", "run", "dev"]

# ✅answer
COPY  package*.json ./
RUN npm install
# comment out COPY and CMD
# COPY . .
# ...
# CMD ["npm", "run", "dev"]

and add command to docker-compose

# docker-compose.yaml
services:
  svelte:
    # ...
    command: npm dev

and rest of configs in the question are not necessary. you can check this out from my working demo at Github

Edit

I just did this, but when running it I'm getting Error: Cannot find module '/app/npm dev'.

the answer uses arbitrary settings. the volumes and CMD may has to be changed accordingly.

i.e.)

# docker-compose.yaml
volumes:
  - ./src:/$YOUR_APP_DIR/src
  - ./static:/$YOUR_APP_DIR/static
  # ...

I've used /home/node/app as WORKDIR because /home/node is used as main WORKDIR for official node docker image. However, it is not necessary to use the same folder. If you're going to use /home/node/app, make sure create the folder before use.

RUN mkdir -p /home/node/app
WORKDIR /home/node/app
sungryeol
  • 3,677
  • 7
  • 20
  • I just did this, but when running it I'm getting `Error: Cannot find module '/app/npm dev'`. – MrCujo Dec 16 '22 at 18:58
  • @MrCujo did you try run my github demo as it is? it sure does work. the answer is proving points by using my own settings. change the volume accordingly – sungryeol Dec 17 '22 at 06:18
  • 1
    thanks a lot @sungryeol!!! It finally worked! only thing was that I had to remove `command: npm dev` from `docker-compose` and leave instead the `CMD` command in my `Dockerfile`, otherwise I'd get an error saying: ```node:internal/modules/cjs/loader:1029 throw err; Error: Cannot find module '/app/npm run dev'``` Other than that all your suggestions made it work. I appreciate your help. Wanted to grant you the points but the bounty had expired, I even tried to reinstate it again but couldn't do it. – MrCujo Dec 18 '22 at 20:15
  • 1
    Never mind, was able to open the bounty again, although my previous bounty was for 50 and it didn't let me choose 50 again, had to do it for 100, but what the heck, you deserve it for your help. I have to wait 23 hours before being able to grant it though. – MrCujo Dec 18 '22 at 20:17
  • Unfortunately it seems like using "Rancher Desktop" as a "Docker Desktop" alternative also causes file updates not to trigger a reload. I've cloned your git repo and it does not work :( I need to use 'usePolling' setting in vite.config.json. – Leon Jan 26 '23 at 13:43
  • @Leon using Rancher is out of scope of this discussion. You should pose a new question. – sungryeol Jan 29 '23 at 08:39
  • @MrCujo I think it's 'run': `npm run dev` – digout Mar 02 '23 at 04:11
  • @Leon did you get anywhere with Rancher Desktop? (same problem) – digout Mar 03 '23 at 12:03
-2

Why are you using docker for local development? Check this https://stackoverflow.com/a/70159286/3957754

Nodejs works very well even on windows, so my advice is to develop directly in the host with a simple nodejs installation. Your hot reload should work.

Docker is for your test, staging or production servers in which you don't want a hot-reload because reals users are using your web. Hot reload is only for local development.

container process

When the docker starts, is linked to a live and foreground process. If this process ends or is restarted, the entire container will crash. Check this https://stackoverflow.com/a/68593731/3957754

That's why docker goal is not related to hot-reload at source code level

anyway

Anyway, if you have a machine in which to have the workspace (nodejs, git, etc) is so complex, you could use docker with nodejs hot reload following these steps:

  • Don't use Dockerfile, use directly docker run ubuntu .... You are working with nodejs not with c#, php or similar follies
  • At the root of your workspace (package.json) execute this
docker run -p 8080:8080 -v $(pwd):/src node:19-alpine bash
  • The previous sentence will create a container not linked to a tcp process. So you will have new sub-shell, with nodejs 19 and alpine ready to use
  • Execute the classic
cd /src
npm install
npm run dev
  • If your app works fine, the hot reload should work. If don't work, try without docker and share us the result
JRichardsz
  • 14,356
  • 6
  • 59
  • 94
  • Docker is used to encapsulate a application's environment requirements for portability. If your host computer is windows and your teammate is linux, you want to use docker. – RockyK Jun 24 '23 at 19:25