💾 Archived View for silbernagel.dev › gemlog › phoenix-tailwind-docker captured on 2023-11-04 at 11:11:52. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-09-08)
-=-=-=-=-=-=-
__ __ __ __ ___ _/ / ___ ____ ___ __ _____/ /__ ___ _/ /_/ / / _ `/ _ \/ _ `/ _ \/ _ \/ // / _ / -_) _ `/ __/ _ \ \_,_/_//_/\_,_/ .__/ .__/\_, /\_,_/\__/\_,_/\__/_//_/ /_/ /_/ /___/
Posted on Mon, June 21 2021
This is no longer an issue since Phoenix started bundling Tailwind.
I was having issues with tailwind building correctly only in production and only when built in a Docker container.
I followed an article I found online 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 version of Phoenix documented 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`.