dockervue.jsnginxvuejs3vite

Status error 500 when deploying Vue web app with Vite and Nginx (within docker compose)


I'm currently improving my web development skills and working on a project using the following tech stack:

I've deployed the app locally, but when I try to access http://localhost:8080, I encounter a 500 Internal Server Error. I’ve checked the browser's dev tools, but the sources tab doesn’t provide any additional information, making it difficult to identify the issue.

I’ve included the project repo here for reference.

I’m not sure where the problem lies — whether it’s with my Nginx configuration, Vite build process, or something else in the Vue app. I’d really appreciate any guidance or hints on how to debug this error.

Thanks in advance for your help!


This is the folder structure:

├── Makefile
├── app
│   ├── capacitor
│   │   └── capacitor.config.ts
│   ├── dist
│   ├── # NOTE: This directory is built from the `web-builder` docker compose service
│   │   ├── css
│   │   │   └── chunk-vendors.a51efc20.css
│   │   ├── index.html
│   │   └── js
│   │       ├── app.38461d29.js
│   │       ├── app.38461d29.js.map
│   │       ├── chunk-vendors.6a82c935.js
│   │       └── chunk-vendors.6a82c935.js.map
│   ├── package-lock.json
│   ├── package.json
│   ├── public
│   │   └── index.html
│   ├── src
│   │   ├── App.vue
│   │   ├── main.js
│   │   ├── router
│   │   │   └── index.js
│   │   └── views
│   │       └── Home.vue
│   └── vite.config.js
├── docker-compose.yml
├── dockerfiles
│   ├── android-build.Dockerfile
│   ├── web-build.Dockerfile
│   ├── web-serve.Dockerfile
├── scripts
│   └── generate-env.sh
└── server
    └── nginx.conf

This is the dockerfile responsible for building the dist (web-build.Dockerfile):

FROM node:20-alpine AS build
COPY app /app
WORKDIR /app
RUN npm install && npm install @vue/cli-service
CMD npx vue-cli-service build

This is the dockerfile responsible for deploying the dist (web-serve.Dockerfile):

FROM nginx:stable-alpine
COPY /app/dist /usr/share/nginx/html/
COPY /server/nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

And this is the relevant part of the docker compose file that puts them together:

services:
  web-builder:
    build:
      dockerfile: dockerfiles/web-build.Dockerfile
    volumes:
      - ./app/dist:/app/dist

  web-server:
    build:
      dockerfile: dockerfiles/web-serve.Dockerfile
    ports:
       - "8080:80"

This is the vite.config.js file:

import {defineConfig} from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
    plugins: [vue()],
    // base: './',
    root: '.',
    build: {
        outDir: 'dist',
        emptyOutDir: true,
        rollupOptions: {
            input: {
                main: 'public/index.html'
            }
        },
        manifest: true
    },
    publicPath: '/'
});

This is the index.html file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Urban Umbrella</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="../src/main.js"></script>
</body>
</html>

This is the main.js file:

import {createApp} from 'vue';
import App from './App.vue';

import {createRouter, createWebHistory} from 'vue-router';
import routes from './router';

import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap-vue-next/dist/bootstrap-vue-next.css';

import {createBootstrap} from 'bootstrap-vue-next';

const router = createRouter({
    // history: createWebHistory(),
    routes,
});

const app = createApp(App);

app.use(createBootstrap());
app.use(router);

app.mount('#app');

This is the App.vue file:

<template>
  <div id="app">
    <router-view></router-view>
  </div>
</template>

<script>
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'App',
});
</script>

This is the router.js file:

import {createRouter, createWebHistory} from 'vue-router';
import Home from '../views/Home.vue';

const routes = [
    {path: '/', component: Home},
];

const router = createRouter({
    history: createWebHistory(),
    routes,
});

export default router;

Finally, this is config.nginx:

user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events
{
    worker_connections 1024;
}
http
{
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';
    access_log /var/log/nginx/access.log main;
    sendfile on;
    keepalive_timeout 65;
    server
    {
        listen 80;
        server_name localhost;
        location /
        {
            root /app;
            index index.html;
            try_files $uri $uri/ /index.html;
        }
        error_page 500 502 503 504 /50x.html;
        location = /50x.html
        {
            root /usr/share/nginx/html;
        }
    }
}

Solution

  • Vite is officially recommended, while Vue CLI is still usable but unmaintained. This is the project that was generated from Vite template that was slightly modified and switched to Vue CLI for some reason. It makes sense to keep using Vite, unless there are reasons to not do this.

    The modifications that are needed to make it regular Vite template that is expected to work normally:

    Make sure that generated dist conforms your expectations, it's supposed to be viewable apart from Docker container when served with any server that is configured for client-side routing, e.g. npx serve --single dist.

    The project has a problem, it creates a router twice, in main.js it should be import router from './router' instead of import routes from './router', and const router ... variable should be removed.