Using dev container with Redwood

Has anyone had any luck with running Redwood using Docker and dev containers?

Context:
I got a new computer and wanted to see if I could could setup Redwood and have a solid development environment all self-contained in docker.

Using the default Node app template in VSCode and exposing the :8910 and :8911 gets me 90% there.

Current Blocker:
The web app is unable to talk with the api, the proxy that is handled by Webpack Dev Server behind the scenes doesn’t seem to work as expected within Docker, requests to that should be forwarded to the api aren’t but both web and api are accessible individually.

Has anyone successfully got this working?

Hi @Darth-Knoppix! Welcome to the forums :wave:

Have you seen this previous topic on the subject? Running in Docker - #7 by nerdstep Anything in there that helps at all?

1 Like

Are they not forwarded at all, or are they forwarded to the wrong url? (what url in that case?)

Thanks for such a quick reply!

I had a look at the post you mentioned and it helped a bit but the proxying issue seemed to be solved with Nginx. (My Nginx knowledge is limited)

Requests from the web app are hitting this url, http://localhost:8910/.netlify/functions/graphql and aren’t being forwarded to the backend api.

I’ll try tinkering with config and Nginx as a last resort and share where I get to.

localhost inside docker or on your own machine?

I’m using localhost:8910 on my host machine to access the web frontend. It’s accessible as is localhost:8911 but when I request a page that performs a fetch to the API, it fails.

This is being served by the yarn rw dev command within a container, for reference here is the config I’m using Redwood Dev Container · GitHub

This is what the browser request looks like:

URL: http://localhost:8910/.redwood/functions/graphql
Status: 500 Internal Server Error
Source: Network
Address: ::1:8910

The response from the dev server is [HPM] Error occurred while trying to proxy request /graphql from localhost:8910 to http://[::1]:8911 (EADDRNOTAVAIL) (https://nodejs.org/api/errors.html#errors_common_system_errors)

The address http://[::1]:8911 is also accessible on my host.

I’ve used docker like once five years ago. So I’m sorry, but I can’t help you any further. @jeliasson Do you have any ideas?

1 Like

I run RedwoodJS with Docker images in production but not for the development workflow. When it comes to the proxying part, I submitted a issue along with a PR to extend the @redwoodjs/api-server package with a routePrefix option. See the issue for the motivation behind that. After that I set the apiProxyPath to "/api" in redwood.toml. Now I proxy / to web containers and /api to the api containers.

I think you’re already set with the Dockerfiles, but here is what I do for production.

api/Dockerfile

###################################################################################################################
# Runner: node
###################################################################################################################

FROM node:12 as runner

# Node
ARG NODE_ENV
ARG RUNTIME_ENV

ENV NODE_ENV=$NODE_ENV
ENV RUNTIME_ENV=$RUNTIME_ENV

ARG DATABASE_URL
ENV DATABASE_URL=$DATABASE_URL

# Set workdir
WORKDIR /app

COPY api api
COPY .nvmrc .
COPY babel.config.js .
COPY graphql.config.js .
COPY package.json .
COPY redwood.toml .
COPY yarn.lock .

# Debug
RUN date

# Install dependencies
RUN yarn install --frozen-lockfile

# Build
RUN yarn rw build api

# Migrate database
RUN yarn rw db up --no-db-client --auto-approve

# Seed database
RUN yarn rw db seed

# Clean up
RUN rm -rf ./api/src

# Set api as workdirectory
WORKDIR /app/api

# Expose RedwoodJS api port
EXPOSE 8911

# Entrypoint to @redwoodjs/api-server binary
ENTRYPOINT [ "yarn", "api-server", "--functions", "./dist/functions", "--port", "8911", "--routePrefix", "/api" ]

web/Dockerfile

###################################################################################################################
# Builder: node
###################################################################################################################

FROM node:12 as builder

# Node
ARG NODE_ENV
ARG RUNTIME_ENV

ENV NODE_ENV=$NODE_ENV
ENV RUNTIME_ENV=$RUNTIME_ENV

# Application specific (Azure Active Directory authentication)
ARG AZURE_ACTIVE_DIRECTORY_CLIENT_ID
ARG AZURE_ACTIVE_DIRECTORY_AUTHORITY
ARG AZURE_ACTIVE_DIRECTORY_REDIRECT_URI
ARG AZURE_ACTIVE_DIRECTORY_LOGOUT_REDIRECT_URI

ENV AZURE_ACTIVE_DIRECTORY_CLIENT_ID=$AZURE_ACTIVE_DIRECTORY_CLIENT_ID
ENV AZURE_ACTIVE_DIRECTORY_AUTHORITY=$AZURE_ACTIVE_DIRECTORY_AUTHORITY
ENV AZURE_ACTIVE_DIRECTORY_REDIRECT_URI=$AZURE_ACTIVE_DIRECTORY_REDIRECT_URI
ENV AZURE_ACTIVE_DIRECTORY_LOGOUT_REDIRECT_URI=$AZURE_ACTIVE_DIRECTORY_LOGOUT_REDIRECT_URIv

# Set workdir
WORKDIR /app

#COPY api .
COPY web web
COPY .nvmrc .
COPY babel.config.js .
COPY graphql.config.js .
COPY package.json .
COPY redwood.toml .
COPY yarn.lock .

# Install dependencies
RUN yarn install --frozen-lockfile

# Build
RUN yarn rw build web

# Clean up
RUN rm -rf ./web/src

###################################################################################################################
# Runner: Nginx
###################################################################################################################

FROM nginx as runner

# Copy dist
COPY --from=builder /app/web/dist /usr/share/nginx/html

# Copy nginx configuration
COPY web/config/nginx/default.conf /etc/nginx/conf.d/default.conf

# List files
RUN ls -lA /usr/share/nginx/html

# Expose RedwoodJS web port
EXPOSE 8910

web/config/nginx/default.conf

server {
    listen 8910 default_server;
    root /usr/share/nginx/html;
    location / {
        try_files $uri $uri/ /index.html;
    }
}

I run my workload in Kubernetes, so below is just a reference to the proxying part.

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: app
  annotations:
    kubernetes.io/ingress.class: "nginx"
    cert-manager.io/cluster-issuer: "letsencrypt"
spec:
  tls:
    - hosts:
        - example.com
      secretName: example-tls
  rules:
    - host: example.com
      http:
        paths:
          - path: /
            backend:
              serviceName: web
              servicePort: 8910
          - path: /api
            backend:
              serviceName: api
              servicePort: 8911

Hope this helps, and let me know if you want the remaining Kubernetes (and Kustomize) files.

1 Like

Thanks for your help @Tobbe and @jeliasson, It looks like Nginx is the only way to get this working as expected. I think I’ll sideline this for now. I may revisit this when I have a better grasp of how Nginx and dev containers work with multiple services.

I’m a little late to the party @Darth-Knoppix but I think you need to get a bit fancier with how you setup Webpack to handle ports and hosts. Something like the topics in this Issue might do the trick:

Hope there’s something helping in the thread :crossed_fingers:

1 Like

I had the same issue with the http://[::1]:8911 (EADDRNOTAVAIL) error with a clean redwood install in a devcontainer. I was able to solve it adding the following config. I think docker needs some extra config to forward ipv6.

Here’s the line that this ENV var affects redwood/webpack.development.js at main · redwoodjs/redwood · GitHub

// .env
RWJS_DEV_API_URL=http://localhost

For years I’ve been told to listen on 0.0.0.0 inside the image, not to localhost… (if I want to see from outside) macos - Localhost vs 0.0.0.0 with Docker on Mac OS - Stack Overflow – (but I’m old and slow : )

Can confirm that configuring the web and api host to listen on 0.0.0.0 doesn’t solve the issue. Thanks for the idea though. The proxy still can’t resolve the ipv6 url inside the container. It can be enabled (Enable IPv6 support | Docker Documentation) but I’m more comfortable with the solution I understand better for now.

The RWJS_DEV_API_URL var is a configuration for the proxy, the url that the “web” service will use to make calls to the “api” service. Both of these services are inside the devcontainer so the address is local. You can still set the host to 0.0.0.0 for external access if you want to. Either way this url should still be localhost.