I have a project with docker and bun, a website and an api, both are compiled with bun. The website is with Astro and the API is just Bun.
The problem I have is that it does not detect the changes in the files no matter how much I have tried every way I have found on the internet.
It should be noted that if I start the projects independently, they do detect the hot reload.
I will leave the files that I use for this project:
Structure:
.
├── README.md
├── api
│ ├── Dockerfile
│ ├── bun.lockb
│ ├── package.json
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── docker-compose.yml
├── nginx
│ └── nginx.conf
└── web
├── Dockerfile
├── README.md
├── astro.config.mjs
├── bun.lockb
├── index.html
├── package.json
├── public
│ └── favicon.svg
├── src
│ ├── components
│ │ ├── Card.astro
│ │ ├── Post.vue
│ │ ├── Posts.vue
│ │ ├── atoms
│ │ │ └── Button.vue
│ │ ├── molecules
│ │ ├── organisms
│ │ └── templates
│ ├── env.d.ts
│ ├── layouts
│ │ └── Layout.astro
│ ├── pages
│ │ ├── api
│ │ │ └── posts
│ │ │ ├── [id].ts
│ │ │ └── index.ts
│ │ ├── index.astro
│ │ └── posts
│ │ ├── [id].astro
│ │ └── index.astro
│ └── utils
├── tsconfig.json
└── tsconfig.node.json
17 directories, 28 files
# Path: docker-compose.yml
version: '3.8'
networks:
cv_network:
driver: bridge
volumes:
web_node_modules:
api_node_modules:
services:
web:
container_name: cv_web
build: web
image: web
restart: "no"
networks:
- cv_network
volumes:
- ./web/:/app
- web_node_modules:/app/node_modules
ports:
- 3000:3000
env_file:
- project.env
api:
container_name: cv_api
build: api
image: api
restart: "no"
networks:
- cv_network
volumes:
- ./api/:/app
- api_node_modules:/app/node_modules
ports:
- 8000:8000
nginx:
container_name: cv_nginx
image: nginx:1.25.2-alpine-slim
networks:
- cv_network
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- web
- api
ports:
- 7800:7800
# Path: api/Dockerfile
FROM oven/bun:1.0.3-slim
WORKDIR /api
COPY package*.json bun.lockb ./
RUN bun install
COPY . .
CMD [ "bun", "run", "dev" ]
Path: api/package.json
{
"name": "bun_app",
"version": "1.0.50",
"scripts": {
"test": "echo "Error: no test specified" && exit 1",
"dev": "bun --watch run src/index.ts"
},
"dependencies": {
},
"devDependencies": {
"bun-types": "1.0.3"
},
"module": "src/index.js"
}
// Path: api/src/index.ts
import { type Serve } from "bun";
type Middleware = (req: any, res: any, next: () => void) => void;
type RequestHandler = (req: any, res: any) => Response;
const loggerMiddleware: Middleware = (req, res, next) => {
const url = new URL(req.url);
const method = req.method;
const currentTime = new Date().toISOString();
console.log(`[${currentTime}] ${method} - ${url.pathname}`);
next();
};
class Router {
routes: { [method: string]: { [path: string]: RequestHandler } } = {};
get(path: string, handler: RequestHandler) {
this.addRoute("GET", path, handler);
}
post(path: string, handler: RequestHandler) {
this.addRoute("POST", path, handler);
}
put(path: string, handler: RequestHandler) {
this.addRoute("PUT", path, handler);
}
private addRoute(method: string, path: string, handler: RequestHandler) {
if (!this.routes[method]) {
this.routes[method] = {};
}
this.routes[method][path] = handler;
}
}
class Server {
middlewares: Middleware[] = [];
routes: { [method: string]: { [path: string]: RequestHandler } } = {};
use(middleware: Middleware) {
this.middlewares.push(middleware);
}
get(path: string, handler: RequestHandler) {
this.addRoute("GET", path, handler);
}
post(path: string, handler: RequestHandler) {
this.addRoute("POST", path, handler);
}
// Agregar más métodos HTTP según sea necesario
private addRoute(method: string, path: string, handler: RequestHandler) {
if (!this.routes[method]) {
this.routes[method] = {};
}
this.routes[method][path] = handler;
}
useRouter(path: string, router: Router) {
for (const [method, routes] of Object.entries(router.routes)) {
for (const [routePath, handler] of Object.entries(routes)) {
const fullPath = path + routePath;
this.addRoute(method, fullPath, handler);
}
}
}
async fetch(request: Request, server: Server): Promise<Response> {
try {
if (!request || !server) {
console.error("Request o Server no definidos");
return new Response("Internal Server Error", { status: 500 });
}
const url = new URL(request.url);
const pathname = url.pathname;
const method = request.method;
let response: Response;
// Ejecutamos los middlewares en orden
for (const middleware of this.middlewares) {
middleware(request, response, () => {});
}
if (this.routes[method] && this.routes[method][pathname]) {
const handler = this.routes[method][pathname];
response = handler(request, new Response(""));
} else {
throw new Error("Route not found"); // Lanza un error si la ruta no se encuentra
}
return response;
} catch (error) {
console.error(`Error: ${error.message}`);
return new Response(error.message, { status: 500 });
}
}
}
const app = new Server();
app.use(loggerMiddleware);
// Agregar middlewares
app.use((req, res, next) => {
console.log("Middleware 1");
next();
});
// Agregar rutas
app.get("/", (req, res) => {
res = new Response("Hello, world!", { status: 200 });
return res;
});
const router = new Router();
router.get("", (req, res) => {
return new Response(JSON.stringify({ message: "aaaaaaaaaa" }), {
status: 200,
});
});
router.post("", (req, res) => {
return new Response(JSON.stringify({ message: "Router POST succeed" }), {
status: 200,
});
});
router.put("", (req, res) => {
return new Response(JSON.stringify({ message: "Router PUT succeed" }), {
status: 200,
});
});
app.useRouter("/test", router);
// Iniciar el servidor
export default {
port: 8000,
fetch(req) {
return app.fetch(req, app);
},
error(error: Error) {
return new Response(`<pre>${error}n${error.stack}</pre>`, {
headers: {
"Content-Type": "text/html",
},
});
},
} satisfies Serve;
# Path: nginx/nginx.conf
worker_processes 1;
events {
worker_connections 1024;
}
http {
# proxy_cache_path /var/cache/nginx keys_zone=astro_ssg_cache:1m inactive=5m max_size=512m;
upstream web {
server web:3000;
}
upstream api {
server api:8000;
}
server {
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;
gzip_min_length 1000;
listen 7800;
server_name localhost;
location / {
proxy_pass http://web;
proxy_http_version 1.1;
# proxy_cache astro_ssg_cache;
# proxy_cache_key $uri;
# proxy_cache_valid 5m;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_redirect off;
}
}
# Para la API
server {
listen 7800;
server_name api.localhost;
location / {
proxy_pass http://api; # Suponiendo que "api" es el nombre del servicio en docker-compose.yml
# ... (otros ajustes de proxy aquí)
}
}
# Para el panel de administración
server {
listen 7800;
server_name admin.localhost;
location / {
proxy_pass http://web; # Asumiendo que "admin" es otro servicio en docker-compose.yml
# ... (otros ajustes de proxy aquí)
}
}
# Para otros subdominios dinámicos
server {
listen 7800;
server_name ~^(?<subdomain>.+).localhost$;
location / {
proxy_pass http://web; # Redirige todos los demás subdominios al servicio "web"
# ... (otros ajustes de proxy aquí)
}
}
}
- SO: Windows 11
- WSL2:
- Distributor ID: Ubuntu
- Description: Ubuntu 20.04.6 LTS
- Release: 20.04
- Codename: focal
2
Answers
Solution to the Hot Reload Issue
After reviewing the files, I found the differences that were causing the Hot Reload issue. Here are the necessary changes to make everything work as expected.
docker-compose.yml
First, in the
docker-compose.yml
file, I needed to update the volume paths to match the working directories in the containers.Before:
After:
Dockerfile in
web
andapi
package.json in
api
Finally, in the
package.json
in theapi
folder, change thedev
script to use the--hot
option.Before:
After:
Summary
docker-compose.yml
.dev
script inapi/package.json
to use--hot
.With these changes, Hot Reload works as expected. 😎
Thanks to Revolucion for Monica who gave me the idea in this comment on SO.
Can you change the –watch with –hot. Hot will soft reload the code.