Unable to serve static images from /priv/static

I’m failing to get my static resources rendered when I deploy this on fly.io. I’m not sure why this Plug.Static configuration is not being respected. What I’m trying to do is pretty vanilla but for some reason the /priv/static resources (like css and js) files are not found after deployment.

Does anyone have any suggestions as to what to try? Thank you

endpoint.ex

  plug Plug.Static,
    at: "/",
    from: :myapp,
    gzip: false,
    only: ~w(assets fonts images favicon.ico robots.txt)

Dockerfile

# Find eligible builder and runner images on Docker Hub. We use Ubuntu/Debian instead of
# Alpine to avoid DNS resolution issues in production.
#
# https://hub.docker.com/r/hexpm/elixir/tags?page=1&name=ubuntu
# https://hub.docker.com/_/ubuntu?tab=tags
#
#
# This file is based on these images:
#
#   - https://hub.docker.com/r/hexpm/elixir/tags - for the build image
#   - https://hub.docker.com/_/debian?tab=tags&page=1&name=bullseye-20230522-slim - for the release image
#   - https://pkgs.org/ - resource for finding needed packages
#   - Ex: hexpm/elixir:1.14.5-erlang-25.3.2-debian-bullseye-20230522-slim
#
ARG ELIXIR_VERSION=1.14.5
ARG OTP_VERSION=25.3.2
ARG DEBIAN_VERSION=bullseye-20230522-slim

ARG BUILDER_IMAGE="hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}"
ARG RUNNER_IMAGE="debian:${DEBIAN_VERSION}"

FROM ${BUILDER_IMAGE} as builder

# install build dependencies
RUN apt-get update -y && apt-get install -y build-essential git \
    && apt-get clean && rm -f /var/lib/apt/lists/*_*

# prepare build dir
WORKDIR /app

# install hex + rebar
RUN mix local.hex --force && \
    mix local.rebar --force

# set build ENV
ENV MIX_ENV="prod"

# install mix dependencies
COPY mix.exs mix.lock ./
RUN mix deps.get --only $MIX_ENV
RUN mkdir config

# copy compile-time config files before we compile dependencies
# to ensure any relevant config change will trigger the dependencies
# to be re-compiled.
COPY config/config.exs config/${MIX_ENV}.exs config/
RUN mix deps.compile

COPY priv priv

COPY lib lib

COPY assets assets

RUN cd assets

# compile assets
RUN mix assets.deploy

# Compile the release
RUN mix compile

# Changes to config/runtime.exs don't require recompiling the code
COPY config/runtime.exs config/

COPY rel rel
RUN mix release

# start a new build stage so that the final image will only contain
# the compiled release and other runtime necessities
FROM ${RUNNER_IMAGE}

RUN apt-get update -y && apt-get install -y libstdc++6 openssl libncurses5 locales \
  && apt-get clean && rm -f /var/lib/apt/lists/*_*

# Set the locale
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen

ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8

WORKDIR "/app"
RUN chown nobody /app

# set runner ENV
ENV MIX_ENV="prod"

# Only copy the final release from the build stage
COPY --from=builder --chown=nobody:root /app/_build/${MIX_ENV}/rel/myapp ./

USER nobody

CMD ["/app/bin/server"]
# Appended by flyctl
ENV ECTO_IPV6 true
ENV ERL_AFLAGS "-proto_dist inet6_tcp"

1 Like

I had to make a directory priv/static/static and put my files in there. Then, I could access it at your-project.com/static/your-file.txt

1 Like

Take a look at the Plug.Static configuration in endpoint.exs. The root folder as configured by phoenix generators requires whitelisting.

2 Likes

I guess I should think before I speak. In retrospect, the static directory I described in my previous post did not magically just work. I had to add it in lib/your_project_web.ex:

def static_paths, do: ~w(assets fonts images favicon.ico robots.txt static)

For Fly specifically, I’d recommend using the statics config in your fly.toml (docs on Fly here). Doing this will bypass Elixir altogether and have the Fly Proxy deliver your static assets straight to clients.

Here’s an example taken from my production Elixir app’s config:

In my Dockerfile (note I’m using a multi-stage build so copying from builder, which may not be relevant to you):

COPY --from=builder --chown=nobody:root /app/priv/static ./static

In my fly.toml:

[[statics]]
guest_path = "/app/static/assets"
url_prefix = "/assets"

[[statics]]
guest_path = "/app/static/favicons"
url_prefix = "/favicons"

[[statics]]
guest_path = "/app/static/images"
url_prefix = "/images"

In my [app]_endpoint.ex endpoint config:

  def static_paths, do: ~w(assets fonts images robots.txt favicons images)
1 Like