What I’d like to do is have 2 Angular apps on the same domain, but serve different files for different paths. For example, browsing to “app1/” will show different content than that on the “app1/test” path. Note: these are default Angular apps, they’re not doing anything special. The only thing I’ve changed the landing page to say “app1” or “app2” to verify that paths hit the correct container.
Additionally, I want anything on the app1/test path to resolve to the same host. So paths like “app1/test” and “app1/test/page1” will lead to the same container. I can get to app1/ with no issues, but I can’t seem to figure out how to route to app1/test correctly. I can browse to it, but it serves the content of app1/ instead of what it’s supposed to. I’ve verified that all of the files I’m creating are inside the container as well, they’re just not being accessed.
Finally, the URL information needs to be in a state that allows nginx to route to the correct container and also allow the angular apps to handle its own virtual routing, without these two interfering with one another.
Here are my config files:
docker-compose.yml
services:
traefik:
image: "traefik:v2.4"
container_name: "traefik"
command:
- "--log.level=DEBUG"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
app1:
build:
context: ./app1
ports:
- "8081:80"
labels:
- "traefik.enable=true"
- "traefik.http.routers.app1.entrypoints=web"
- "traefik.http.routers.app1.rule=Host(`localhost`)"
- "traefik.http.routers.app1.middlewares=app1-stripprefix"
- "traefik.http.routers.app1.middlewares=app1-autodetect"
- "traefik.http.middlewares.app1-stripprefix.stripprefix.prefixes=/"
- "traefik.http.middlewares.app1-autodetect.contenttype.autodetect=false"
- "traefik.port=80"
app2:
build:
context: ./app2
ports:
- "8082:80"
labels:
- "traefik.enable=true"
- "traefik.http.routers.app2.entrypoints=web"
- "traefik.http.routers.app2.rule=Host(`localhost`) && PathPrefix(`/test{regex:$$|/.*}`)"
- "traefik.http.routers.app2.middlewares=app2-stripprefix"
- "traefik.http.routers.app2.middlewares=app2-autodetect"
- "traefik.http.middlewares.app2-stripprefix.stripprefix.prefixes=/test"
- "traefik.http.middlewares.app2-autodetect.contenttype.autodetect=false"
- "traefik.port=80"
Dockerfile for both Angular apps
# set working directory
WORKDIR /app
# add `/app/node_modules/.bin` to $PATH
ENV PATH /app/node_modules/.bin:$PATH
# install and cache app dependencies
COPY package.json /app/package.json
RUN npm install
RUN npm install -g @angular/cli@11.0.7
# add app
COPY . /app
# generate build
RUN ng build --output-path=dist
# base image
FROM nginx:1.16.0-alpine
# copy artifact build from the 'build environment'
COPY --from=build /app/dist /usr/share/nginx/html
COPY ./nginx/nginx.conf /etc/nginx/conf.d/default.conf
# expose port 80
EXPOSE 80
# run nginx
CMD ["nginx", "-g", "daemon off;"]
nginx.conf for app1
include /etc/nginx/mime.types;
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
nginx.conf for app2
include mime.types;
include /etc/nginx/mime.types;
server {
listen 80;
server_name localhost;
location /test {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
I solved my problem. There were a few edits I made along the road, but the final key to the puzzle was a "try_files" line in my nginx.conf files. Read about it here: https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
Here are the updated files:
docker-compose.yml
services:
traefik:
image: "traefik:v2.4"
container_name: "traefik"
command:
- "--log.level=DEBUG"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
app1:
build:
context: ./app1
ports:
- "8081:80"
labels:
- "traefik.enable=true"
- "traefik.http.routers.app1.entrypoints=web"
- "traefik.http.routers.app1.rule=Host(`localhost`)"
- "traefik.http.routers.app1.middlewares=app1-stripprefix"
- "traefik.http.routers.app1.middlewares=app1-autodetect"
- "traefik.http.middlewares.app1-stripprefix.stripprefix.prefixes=/"
- "traefik.http.middlewares.app1-autodetect.contenttype.autodetect=false"
- "traefik.port=80"
app2:
build:
context: ./app2
ports:
- "8082:80"
labels:
- "traefik.enable=true"
- "traefik.http.routers.app2.entrypoints=web"
- "traefik.http.routers.app2.rule=PathPrefix(`/test{regex:$$|/.*}`)"
- "traefik.http.routers.app2.middlewares=app2-stripprefix"
- "traefik.http.routers.app2.middlewares=app2-autodetect"
- "traefik.http.middlewares.app2-stripprefix.stripprefix.prefixes=/test"
- "traefik.http.middlewares.app2-autodetect.contenttype.autodetect=false"
- "traefik.port=80"
Dockerfile (for app1)
FROM node:14.15.4 as build
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
COPY package.json /app/package.json
RUN npm install
RUN npm install -g @angular/cli@11.0.7
COPY . /app
RUN ng build --output-path=dist
FROM nginx:1.16.0-alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY ./nginx/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Dockerfile for app2
FROM node:14.15.4 as build
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
COPY package.json /app/package.json
RUN npm install
RUN npm install -g @angular/cli@11.0.7
COPY . /app
RUN npm install
RUN npm install --save-dev @angular-devkit/build-angular
RUN ng build --base-href /test --deploy-url /test/ --output-path=dist
FROM nginx:1.16.0-alpine
COPY --from=build /app/dist /usr/share/nginx/html/test
COPY ./nginx/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
nginx.conf for app1
include mime.types;
include /etc/nginx/mime.types;
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
nginx.conf for app2
include mime.types;
include /etc/nginx/mime.types;
server {
listen 80;
server_name localhost;
location /test {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /test/index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
location /.ico {
root /usr/share/nginx/html;
add_header Content-Type image/x-icon;
}
}