Can't get vite with express app to deploy correctly

I am trying to get my vite with express app to deploy but the fly.io URL that I am given only shows a “Welcome to nginx” screen and not my app. I am not sure what I need to tweak, but I would really appreciate some help to get this thing deployed.

Here is my TOML file:

# fly.toml app configuration file generated for reskipper on 2024-08-09T08:53:38-05:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#

app = 'reskipper'
primary_region = 'atl'

[build]

[http_service]
  internal_port = 80
  force_https = true
  auto_stop_machines = 'stop'
  auto_start_machines = true
  min_machines_running = 0
  processes = ['app']

[[vm]]
  memory = '1gb'
  cpu_kind = 'shared'
  cpus = 1

Here is my docker file:

# syntax = docker/dockerfile:1

# Adjust NODE_VERSION as desired
ARG NODE_VERSION=20.9.0
FROM node:${NODE_VERSION}-slim as base

LABEL fly_launch_runtime="Vite"

# Vite app lives here
WORKDIR /app

# Set production environment
ENV NODE_ENV="production"
ARG YARN_VERSION=1.22.17
RUN npm install -g yarn@$YARN_VERSION --force


# Throw-away build stage to reduce size of final image
FROM base as build

# Install packages needed to build node modules
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y build-essential node-gyp pkg-config python-is-python3

# Install node modules
COPY --link package.json yarn.lock ./
RUN yarn install --frozen-lockfile --production=false

# Copy application code
COPY --link . .

# Build application
RUN yarn run build

# Remove development dependencies
RUN yarn install --production=true


# Final stage for app image
FROM nginx

# Copy built application
COPY --from=build /app/dist /usr/share/nginx/html

# Start the server by default, this can be overwritten at runtime
EXPOSE 80
CMD [ "/usr/sbin/nginx", "-g", "daemon off;" ]

Here is my package.json scripts:

   "dev": "vite",
   "build": "tsc -b && vite build",
   "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
   "preview": "vite preview",
   "start-server": "yarn build-server && ts-node -P tsconfig.server.json server/index.ts",
   "build-server": "tsc -p tsconfig.server.json",
   "start": "./start.sh"
 },```

Here is the file that start.sh runs:
```#!/bin/sh

# Build the server first
yarn build-server

# Start the backend server
yarn start-server &

# Start the Vite development server in the background
yarn dev

Basically, I need to build the express server, start it, and then start the vite part of the app to run with it.

Hi,

The first place I would look is the log. That shows what the app is doing internally. Any errors, exceptions, issues etc stopping it running will show up in there. It might show “can’t do X” or “error: missing Y”. That error message will usually get you 99% of the solution. If you run flyctl logs you should see the most recent log lines. The dashboard may show them too.

Just by looking at those files … hmm was that Dockerfile etc generated by Fly? Or have they been copied from elsewhere e.g another app/platform? Only it looks like (at first glance) that nginx is indeed being run (last line of the Dockerfile) and set to listen on port 80. Hence when you go to the app’s URL, it shows the nginx welcome page. So that part is working.

But … you are using that to proxy requests to Express. The Express server also needs to be running, in parallel, and nginx needs to know about it. In your fly.toml it looks like you only have one process listed, the “app”. There are different ways to do this: Multiple processes inside a Fly.io app · Fly Docs … but in short you can either run multiple machines (by listing multiple processes) or run multiple processes in one machine. Of course the second option is cheaper but you would usually need some kind of process manager to keep both running. I’m not sure (may be wrong) that start.sh approach of using & to keep it running in the background is sufficient :thinking:. That is why the logs might help help to see exactly what is going on. It looks to me like only nginx is running.

The other option would be to not use nginx at all. There are arguments for and against using it and this post is long enough already but since Node can run a web server directly you can avoid running nginx. Let Fly’s proxy handle the TLS for you. Your app is then just the Express. For an example of that, this is a (old and JS) demo. Notice how in its Dockerfile, it only runs Node (not nginx too). That way no supervisor or & process is needed: Fly keeps Express going, and its proxy acts as the nginx part:

(Again, if all those files you list were provided by Fly there must be a reason they do use nginx and have gone for that start.sh & approach, and so I’d stick to what they recommend - so it’s back to the logs to see what they have provided is not woring for you)

Thanks for the response, I have been trying to look deeper into it - but all the files I shared were generated by running fly launch.

Here is an example of my logs:

atl [info] Configuring firecracker
atl [info] 2024/08/09 13:55:20 [notice] 319#319: signal 2 (SIGINT) received from 1, exiting
atl [info] 2024/08/09 13:55:20 [notice] 324#324: exiting
atl [info] 2024/08/09 13:55:20 [notice] 324#324: exit
atl [info] INFO Sending signal SIGINT to main child process w/ PID 319
atl [info] 2024/08/09 13:55:20 [notice] 319#319: signal 17 (SIGCHLD) received from 324
atl [info] 2024/08/09 13:55:20 [notice] 319#319: worker process 324 exited with code 0
atl [info] 2024/08/09 13:55:20 [notice] 319#319: exit
atl [info] INFO Main child exited normally with code: 0
atl [info] INFO S[ 55.667980] reboot: Restarting system
atl [info] 2024-08-09T13:55:22.747020892 [01J4VPR9FTTVCZFXTVQ7987J4A:main] Running Firecracker v1.7.0
atl [info] [ 0.275231] PCI: Fatal: No config space access function found
atl [info] INFO Starting init (commit: 20f21dc5f)...
atl [info] INFO Preparing to run: `/docker-entrypoint.sh /usr/sbin/nginx -g daemon off;` as root
atl [info] INFO [fly api proxy] listening at /.fly/api
atl [info] Machine created and started in 3.223s
atl [info] 2024/08/09 13:55:23 INFO SSH listening listen_address=[fdaa:3:bd7f:a7b:e4:66da:bd42:2]:22 dns_server=[fdaa::3]:53
atl [info] 2024/08/09 13:55:23 [notice] 319#319: using the "epoll" event method
atl [info] 2024/08/09 13:55:23 [notice] 319#319: nginx/1.27.0
atl [info] 2024/08/09 13:55:23 [notice] 319#319: built by gcc 12.2.0 (Debian 12.2.0-14)
atl [info] 2024/08/09 13:55:23 [notice] 319#319: OS: Linux 5.15.98-fly
atl [info] 2024/08/09 13:55:23 [notice] 319#319: getrlimit(RLIMIT_NOFILE): 10240:10240
atl [info] 2024/08/09 13:55:23 [notice] 319#319: start worker processes
atl [info] 2024/08/09 13:55:23 [notice] 319#319: start worker process 324
atl [info] App reskipper has excess capacity, autostopping machine 781117df9216d8. 1 out of 2 machines left running (region=atl, process group=app)
atl [info] 2024/08/09 14:02:46 [notice] 319#319: signal 2 (SIGINT) received from 1, exiting
atl [info] 2024/08/09 14:02:46 [notice] 324#324: exiting
atl [info] 2024/08/09 14:02:46 [notice] 324#324: exit
atl [info] INFO Sending signal SIGINT to main child process w/ PID 319
atl [info] 2024/08/09 14:02:46 [notice] 319#319: signal 17 (SIGCHLD) received from 324
atl [info] 2024/08/09 14:02:46 [notice] 319#319: worker process 324 exited with code 0
atl [info] 2024/08/09 14:02:46 [notice] 319#319: exit
atl [info] INFO Main child exited normally with code: 0
atl [info] INFO Starting clean up.
atl [info] WARN could not unmount /rootfs: EINVAL: Invalid argument
atl [info] [ 444.133353] reboot: Restarting system
atl [info] App reskipper has excess capacity, autostopping machine e78432d7f577e8. 0 out of 1 machines left running (region=atl, process group=app)
atl [info] INFO Sending signal SIGINT to main child process w/ PID 319
atl [info] 2024/08/09 14:03:58 [notice] 319#319: signal 2 (SIGINT) received from 1, exiting
atl [info] 2024/08/09 14:03:58 [notice] 324#324: exiting
atl [info] 2024/08/09 14:03:58 [notice] 324#324: exit
atl [info] 2024/08/09 14:03:58 [notice] 319#319: signal 17 (SIGCHLD) received from 324
atl [info] 2024/08/09 14:03:58 [notice] 319#319: worker process 324 exited with code 0
atl [info] 2024/08/09 14:03:58 [notice] 319#319: exit
atl [info] INFO Main child exited normally with code: 0
atl [info] INFO Starting clean up.
atl [info] WARN could not unmount /rootfs: EINVAL: Invalid argument
atl [info] [ 529.200707] reboot: Restarting system

Hi,

Ah, ok, Fly must have made it like that for a reason :slight_smile:

It looks like the machine is being shutdown. However that could be correct (since by default Fly does shutdown idle machines which saves you money). I’d start by disabling that (for now) and so keep one machine running. That way you will be able to see whether it keeps running and if not, why not (not due to Fly turning it off, confusing things). Can switch back once you finish investigating. For how see:

Once you do that, I’d then SSH in to the machine to see what is running fly ssh console

You can run a command to see what processes are running (I always forget the flags but e.g ps -A)

You should see some nginx processes listed (must be, since you get the nginx page) along with your node server. Since both need to be running and stay running. It looks like either the node server is not running or it is but nginx is not correctly proxying requests to it. But those are guesses.

I have not seen anything that suggests that the node server is running. I am thinking that it has not been able to start at all. I have several console.logs in place for server start up and haven’t seen any of them.

It almost seems like instead of my app, its only a blank NGINX server that is starting up and showing their default html.

That could explain it. Running two processes within one machine makes life more complicated. It works but you then have to deal with keeping both running and having one talk to the other.

Since you need (a) the node server to be running and (b) for nginx to know about it (to proxy requests to it). It would only know about it if using a custom conf file. I can’t see a mention of a custom conf being added in the listed files :thinking: Looks to me like (a) and/or (b).

I saw this thread which sounds kind of similar, in that they want nginx + express. Notice the user’s custom nginx conf being referenced at the top. That’s what you would need to run nginx and express as the conf would need some lines to tell nginx how to proxy requests to it. The reply from Fly points out that they can handle TLS for you. It is a good idea to use a proxy in front of node but in this case - since you are using a PaaS not e.g a simple VPS - you already have one:

That way your app would just run node. It’s up to you but unless there is a reason to use nginx personally I’d just run node.

So, are you suggesting rewriting the dockerfile itself so that I can run JUST the node app and not worry about the nginx server? I actually didn’t set up anything for nginx on my own, it didn’t appear in my app until I ran the fly launch command.

What I think is happening is that the scanner detected Vite but not express. The quickest fix is probably to remove the Dockerfile, add a “start” script in package.json that starts your express server, and then rerun the dockerfile generator:

npx dockerfile
1 Like

@Jmartinez Yep.

From @rubys answer Fly can do that rewrite for you :slight_smile: Give that a try and you should be all set. If not, it’s back the log to see what it’s now doing.

Thanks for the direction!

Here is the updated docker file:

# syntax = docker/dockerfile:1

# Adjust NODE_VERSION as desired
ARG NODE_VERSION=20.9.0
FROM node:${NODE_VERSION}-slim as base

LABEL fly_launch_runtime="Vite"

# Vite app lives here
WORKDIR /app

# Set production environment
ENV NODE_ENV="production"
ARG YARN_VERSION=1.22.17
RUN npm install -g yarn@$YARN_VERSION --force


# Throw-away build stage to reduce size of final image
FROM base as build

# Install packages needed to build node modules
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y build-essential node-gyp pkg-config python-is-python3

# Install node modules
COPY --link package.json yarn.lock ./
RUN yarn install --frozen-lockfile --production=false

# Copy application code
COPY --link . .

# Build application
RUN yarn run build

# Remove development dependencies
RUN yarn install --production=true


# Final stage for app image
FROM nginx

# Copy built application
COPY --from=build /app/dist /usr/share/nginx/html

# Start the server by default, this can be overwritten at runtime
EXPOSE 80
CMD [ "/usr/sbin/nginx", "-g", "daemon off;" ]

Updated package.json:

    "dev": "vite",
    "build": "tsc -b && vite build",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview",
    "start-server": "yarn build-server && ts-node -P tsconfig.server.json server/index.ts",
    "build-server": "tsc -p tsconfig.server.json",
    "start-dev": "./start.sh",
    "start": "yarn build-server && ts-node -P tsconfig.server.json server/index.ts"
  },

Message I am getting when trying to deploy:

 => ERROR [build 3/6] RUN yarn install --frozen-lockfile --production=false                                                                 244.8s 
------
 > [build 3/6] RUN yarn install --frozen-lockfile --production=false:
1.330 yarn install v1.22.17
1.675 [1/4] Resolving packages...
2.273 [2/4] Fetching packages...
45.73 info There appears to be trouble with your network connection. Retrying...
88.32 info There appears to be trouble with your network connection. Retrying...
119.3 info There appears to be trouble with your network connection. Retrying...
152.4 info There appears to be trouble with your network connection. Retrying...
185.5 info There appears to be trouble with your network connection. Retrying...
220.9 info If you think this is a bug, please open a bug report with the information provided in "/app/yarn-error.log".
220.9 info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.
220.9 error An unexpected error occurred: "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz: ESOCKETTIMEDOUT".
------
Error: failed to fetch an image or build from source: error building: failed to solve: process "/bin/sh -c yarn install --frozen-lockfile --production=false" did not complete successfully: exit code: 1

Any advice on next steps? Also, will the NGINX server start up the vite part of the code?

Er … well that error can be explained by a network issue. Somewhere between your chosen region and the Yarn registry there is presumably a network issue. It looks like it tried five times to install the packages, couldn’t, and gave up. So that’s a completely different issue to the last one.

It could be worth trying again if it’s a temporary fault, or deploying to a different region to bypass it.

Having got that resolved, you would be left with the nginx issue. It looks like that new Dockerfile also runs nginx as a proxy. I don’t know the inner logic of how it’s auto-building that Dockerfile but (total guess) you could shorten your start command to:

"start": "ts-node -P tsconfig.server.json server/index.ts"

… letting build-server be run separately, then re-run the command suggested by @rubys and see what it produces.

Yes, it does look like a temporary network error - oddly one that is occurring on the build machine unless --local-only was passed on the launch/deploy command.

It looks like there are two build steps needed: one for vite and one for express. I’d suggest changing “build” to be “yarn build-server && yarn build-client”

No, there is no need for nginx at all if express is set up to serve the built vite assets as static files.

I updated the start and build scripts according to what y’all suggested. I am still getting that strange network connection. It didn’t start until I rebuilt the dockerfile - not sure if its a red herring or if something is wrong on the dockerfile.

Ah, so yes the changes to the start/build won’t affect that issue. As @rubys says, it’s a network issue. Your last log showed an error on https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz. Out of interest, try loading that in your browser. It should download. You don’t need to open it. It’s just to check you can.

If that works and you have Docker installed locally, then I’d try bypassing that random network issue with Fly’s builder by doing a local build. As @rubys mentions, try appending --local-only. That will build the image locally and so use your network. It takes one variable out of the equation.

Running it locally did indeed get me past the network error - however I am still only getting the NGINX blank site to load in the browser. Here is my updated package.json scripts and dockerfile.

Dockerfile:


# Adjust NODE_VERSION as desired
ARG NODE_VERSION=20.9.0
FROM node:${NODE_VERSION}-slim as base

LABEL fly_launch_runtime="Vite"

# Vite app lives here
WORKDIR /app

# Set production environment
ENV NODE_ENV="production"
ARG YARN_VERSION=1.22.17
RUN npm install -g yarn@$YARN_VERSION --force


# Throw-away build stage to reduce size of final image
FROM base as build

# Install packages needed to build node modules
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y build-essential node-gyp pkg-config python-is-python3

# Install node modules
COPY --link package.json yarn.lock ./
RUN yarn install --frozen-lockfile --production=false

# Copy application code
COPY --link . .

# Build application
RUN yarn run build

# Remove development dependencies
RUN yarn install --production=true


# Final stage for app image
FROM nginx

# Copy built application
COPY --from=build /app/dist /usr/share/nginx/html

# Start the server by default, this can be overwritten at runtime
EXPOSE 80
CMD [ "/usr/sbin/nginx", "-g", "daemon off;" ]

Here are my updated package.json scripts:

  "scripts": {
    "dev": "vite",
    "build-client": "tsc -b && vite build",
    "build": "yarn build-server && yarn build-client",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview",
    "start-server": "yarn build-server && ts-node -P tsconfig.server.json server/index.ts",
    "build-server": "tsc -p tsconfig.server.json",
    "start-dev": "./start.sh",
    "start": "ts-node -P tsconfig.server.json server/index.ts"
  },

Get rid of nginx

Alright, I removed the NGINX stuff. Not getting the NGINX issues anymore, but I am not able to get my app started and getting a 502 on the site itself.

Here are my logs now:

atl [info] error Command failed with exit code 134.
atl [info] info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
atl [info] INFO Main child exited normally with code: 134
atl [info] INFO Starting clean up.
atl [info] WARN could not unmount /rootfs: EINVAL: Invalid argument
atl [info] [ 12.638506] reboot: Restarting system
atl [info] machine did not have a restart policy, defaulting to restart
atl [info] 2024-08-13T01:20:55.222254862 [01J54N56WN2FBNVVD2PW9WX3MJ:main] Running Firecracker v1.7.0
atl [info] [ 0.277030] PCI: Fatal: No config space access function found
atl [info] INFO Starting init (commit: 20f21dc5f)...
atl [info] INFO Preparing to run: `docker-entrypoint.sh yarn run start` as root
atl [info] INFO [fly api proxy] listening at /.fly/api
atl [info] 2024/08/13 01:20:55 INFO SSH listening listen_address=[fdaa:3:bd7f:a7b:e4:66da:bd42:2]:22 dns_server=[fdaa::3]:53
atl [info] Machine started in 804ms
atl [error] [PC01] instance refused connection. is your app listening on 0.0.0.0:8080? make sure it is not only listening on 127.0.0.1 (hint: look at your startup logs, servers often print the address they are listening on)
atl [error] [PC01] instance refused connection. is your app listening on 0.0.0.0:8080? make sure it is not only listening on 127.0.0.1 (hint: look at your startup logs, servers often print the address they are listening on)
atl [info] yarn run v1.22.19
atl [info] $ ts-node -P tsconfig.server.json server/index.ts
atl [error] [PC01] instance refused connection. is your app listening on 0.0.0.0:8080? make sure it is not only listening on 127.0.0.1 (hint: look at your startup logs, servers often print the address they are listening on)
atl [info] <--- Last few GCs --->
atl [info] [349:0x65e59a0] 10697 ms: Mark-Compact (reduce) 483.4 (492.1) -> 482.4 (492.3) MB, 361.13 / 0.00 ms (+ 39.9 ms in 11 steps since start of marking, biggest step 17.1 ms, walltime since start of marking 440 ms) (average mu = 0.205, current mu = 0.103) a[349:0x65e59a0] 11205 ms: Mark-Compact (reduce) 483.3 (492.3) -> 482.9 (492.8) MB, 352.61 / 0.00 ms (+ 63.3 ms in 15 steps since start of marking, biggest step 21.1 ms, walltime since start of marking 436 ms) (average mu = 0.193, current mu = 0.181) a
atl [info] <--- JS stacktrace --->
atl [info] FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
atl [info] 1: 0xc99970 node::Abort() [/usr/local/bin/node]
atl [info] 2: 0xb6ffcb [/usr/local/bin/node]
atl [info] 3: 0xebe9f0 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [/usr/local/bin/node]
atl [info] 4: 0xebecd7 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [/usr/local/bin/node]
atl [info] 5: 0x10d0785 [/usr/local/bin/node]
atl [info] 6: 0x10d0d14 v8::internal::Heap::RecomputeLimits(v8::internal::GarbageCollector) [/usr/local/bin/node]
atl [info] 7: 0x10e7c04 v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::internal::GarbageCollectionReason, char const*) [/usr/local/bin/node]
atl [info] 8: 0x10e841c v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/usr/local/bin/node]
atl [info] 9: 0x10be721 v8::internal::HeapAllocator::AllocateRawWithLightRetrySlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/usr/local/bin/node]
atl [info] 10: 0x10bf8b5 v8::internal::HeapAllocator::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/usr/local/bin/node]
atl [info] 11: 0x109ce26 v8::internal::Factory::NewFillerObject(int, v8::internal::AllocationAlignment, v8::internal::AllocationType, v8::internal::AllocationOrigin) [/usr/local/bin/node]
atl [info] 12: 0x14f7c56 v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [/usr/local/bin/node]
atl [info] 13: 0x1931ef6 [/usr/local/bin/node]
atl [info] Aborted
atl [info] error Command failed with exit code 134.
atl [info] info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
atl [info] INFO Main child exited normally with code: 134
atl [info] INFO Starting clean up.
atl [info] WARN could not unmount /rootfs: EINVAL: Invalid argument
atl [info] [ 13.620904] reboot: Restarting system
atl [info] machine did not have a restart policy, defaulting to restart
atl [info] Starting machine
atl [error] [PM07] failed to change machine state: machine still active, refusing to start
atl [info] 2024-08-13T01:21:07.143089584 [01J54N4NSDBKW8H0R7Q06ZJA1P:main] Running Firecracker v1.7.0

My updated docker file:

# syntax = docker/dockerfile:1

# Adjust NODE_VERSION as desired
ARG NODE_VERSION=20.9.0
FROM node:${NODE_VERSION}-slim as base

LABEL fly_launch_runtime="Vite"

# Vite app lives here
WORKDIR /app

# Set production environment
ENV NODE_ENV="production"
ARG YARN_VERSION=1.22.17
RUN npm install -g yarn@$YARN_VERSION --force

# Install packages needed to build node modules
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y build-essential node-gyp pkg-config python-is-python3

# Install node modules
COPY --link package.json yarn.lock ./
RUN yarn install --frozen-lockfile --production=false

# Copy application code
COPY --link . .

# Build application
RUN yarn run build

# Remove development dependencies
RUN yarn install --production=true

# Remove unnecessary files
RUN rm -rf node_modules && yarn install --production=true && rm -rf /root/.cache

# Final image
FROM node:${NODE_VERSION}-slim as final

WORKDIR /app

COPY --from=base /app /app

EXPOSE 8080
CMD [ "yarn", "run", "start" ]

Ok, yep, now you’re just running Node and not nginx. Much simpler.

As for why it’s not working now, looks like

JavaScript heap out of memory

Try using a machine with more RAM. At least for now. You can then debug why that is e.g the build command. But it would at least get you going.

Thanks for all your help guys! I simplified the docker file and ended up building the image locally and then pushed it up with fly deploy. Then, I hosted my static vite files on cloudflare. So my app is hosted!

Thanks again!

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.