If anyone else stumbles upon this. I got it working.
HOWTO get VUE and vue-router to work on a fly.io machine.
vite.config.js
export default defineConfig({
server: {
host: true, // listen on all adresses, incl. LAN and public adresses
port: 8080,
strictPort: true, // only use this port. Exit if not available
allowedHosts: ["localhost", "127.0.0.1", ".fly.dev"],
},
plugins: [
vue(),
],
})
Dockerfile
# syntax = docker/dockerfile:1
# Adjust NODE_VERSION as desired
ARG NODE_VERSION=22.14.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"
# 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 package-lock.json package.json ./
RUN npm ci --include=dev
# Copy application code
COPY . .
# Build application
RUN npm run build
# Remove development dependencies
RUN npm prune --omit=dev
# Final stage for app image
FROM nginx:alpine
# Set the desired target directory for the built app
WORKDIR /app
# Copy built application
COPY --from=build /app/dist /app
# overwrite nginx /conf.d/default.conf for VUE webHistoryMode special configs
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
# Start the server by default, this can be overwritten at runtime
EXPOSE 80
CMD [ "/usr/sbin/nginx", "-g", "daemon off;" ]
nginx.conf
You need a special nginx conf for vue-router webHistoryMode() to work
# NGINX configuration for VUE SPA
# https://router.vuejs.org/guide/essentials/history-mode.html#nginx
# overwrite /etc/nginx/conf.d/default.conf with this
server {
listen 80;
server_name _;
root /app;
index index.html;
# Main SPA route handling for vue-router in webHistoryMode()
# https://router.vuejs.org/guide/essentials/history-mode.html#nginx
location / {
try_files $uri $uri/ /index.html;
}
# Optional: Improve caching for static assets (e.g. built JS/CSS files)
#location ~* \.(?:ico|css|js|gif|jpe?g|png|woff2?|ttf|svg|mp4|webm)$ {
# expires 6M;
# access_log off;
# add_header Cache-Control "public";
#}
# Optional: Error pages
#error_page 404 /index.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /app;
}
}
# Hint do a /usr/sbin/nginx -s reload after editing this file
fly.tom
# fly.toml app configuration file generated for my-fly-app-dev on 2025-10-10T17:42:23+02:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#
app = 'my-vue-frontend-spa'
primary_region = 'fra'
[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 = '512mb'
cpu_kind = 'shared'
cpus = 1