💾 Archived View for silbernagel.dev › gemlog › phoenix-tailwind-docker captured on 2023-09-28 at 15:39:26. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-09-08)
-=-=-=-=-=-=-
__ __ __ __ ___ _/ / ___ ____ ___ __ _____/ /__ ___ _/ /_/ / / _ `/ _ \/ _ `/ _ \/ _ \/ // / _ / -_) _ `/ __/ _ \ \_,_/_//_/\_,_/ .__/ .__/\_, /\_,_/\__/\_,_/\__/_//_/ /_/ /_/ /___/
Posted on Mon, June 21 2021
I was having issues with tailwind building correctly ONLY in production and ONLY when built in a Docker container.
I followed an online guide to get postcss and tailwind configured correctly so that it only builds the classes required by the application (we don't want a huge css file). My tailwind.config.js looked like this:
module.exports = { purge: [ '../lib/**/*.ex', '../lib/**/*.leex', '../lib/**/*.eex', './js/**/*.js' ], enabled: process.env.NODE_ENV === 'production', mode: 'jit', darkMode: false, // or 'media' or 'class' theme: { extend: { container: (theme) => ({ center: true, padding: theme("spacing.4"), screens: { sm: "100%", md: "100%", lg: "1024px", xl: "1280px" } }) }, }, variants: { extend: {}, }, plugins: [ require('@tailwindcss/typography'), ], }
Here we are telling tailwind to search through our .ex, .leex, .eex and .js files for relevent classes and purge anything not used.
This all worked perfect in development and local testing.
I add a Dockerfile to use as my production deployment stategy. I started with the Phoenix documented version of the Dockerfile and tweaked it a little to use the latest version of Elixir. Here is what I ended up with that was not compiling tailwind correctly:
FROM elixir:1.12.1-alpine AS build RUN apk add --no-cache build-base npm git python3 WORKDIR /app RUN mix local.hex --force && \ mix local.rebar --force ENV MIX_ENV=prod COPY mix.exs mix.lock ./ COPY config config RUN mix do deps.get, deps.compile # build assets COPY assets/package.json assets/package-lock.json ./assets/ RUN npm --prefix ./assets ci --progress=false --no-audit --loglevel=error COPY priv priv COPY assets assets RUN npm run --prefix ./assets deploy RUN mix phx.digest COPY lib lib # uncomment COPY if rel/ exists # COPY rel rel RUN mix do compile, release # prepare release image FROM alpine:3.13 AS app RUN apk add --no-cache openssl ncurses-libs libstdc++ WORKDIR /app RUN chown nobody:nobody /app USER nobody:nobody COPY --from=build --chown=nobody:nobody /app/_build/prod/rel/my_app ./ ENV HOME=/app CMD trap 'exit' INT; ./bin/my_app eval "MyApp.Release.migrate()" && ./bin/my_app start
I tried many different things to fix the issue. I tried installing a specific version of node in the image - building a development build with webpack - but what it came down to was an order of operations bug.
Remember the tailwind config from above? It says to purge any tailwind classes that haven't been used in our template files. Well, our image doesn't have any template files at the time of running npm run --prefix ./assets deploy because we haven't copied our lib folder over to the image yet!
So the simple fix was to move COPY lib lib up in the Dockerfile before running npm deploy.