Problem reading Enviroment variables when deploying with Docker

Hello Fellas,

I am working on a project that uses elixir 1.9 and phoenix 1.5.7 in an ubrella project.

Now in certain live views I need to access enviroment variables which I normally do through
System.get_env("AWS_BUCKET_NAME_IE")

The problem is that when I deploye through docker the variables that suppose to containing enviroment variable content are empty.

But wierdly enough (at least for me) if I log into the container. through

  1. docker exec -it {container_name} bash
  2. ./bin/appname remote

If I try to access the variable from the elixir shell I got, for example executing System.get_env("AWS_BUCKET_NAME_IE")
I get the expected results

The way I ussyally get the name of the variables in the live_view is
@bucket System.get_env("AWS_BUCKET_NAME_IE") and that always has an empty string as result when deploying with docker.

my Docker file looks like (app specific names are changed)

FROM bitwalker/alpine-elixir-phoenix:latest as builder

WORKDIR /app

ENV MIX_ENV=prod \
    SECRET_KEY_BASE=$SECRET_KEY_BASE \
    AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \
    AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \
    AWS_BUCKET_NAME=$AWS_BUCKET_NAME \
    AWS_ASSETS_BUCKET_NAME=$AWS_ASSETS_BUCKET_NAME \
    DATABASE_URL=$DATABASE_URL \
    SHELL=/bin/bash

# install hex + rebar
RUN mix do local.hex --force, local.rebar --force
          
COPY config config
COPY mix.* ./
COPY apps/app/mix.exs ./apps/app/
COPY apps/app_web/mix.exs ./apps/app_web/
          
RUN mix do deps.get --only $MIX_ENV, deps.compile
          
COPY . /app/
        
# build assets
WORKDIR /app/apps/app_web
    
RUN npm install --prefix ./assets/ ci --progress=false --no-audit --loglevel=error
RUN npm run deploy --prefix ./assets
RUN mix phx.digest
      
WORKDIR /app   

RUN PORT=4000 MIX_ENV=prod mix do compile, phx.swagger.generate, release

# prepare release image
FROM bitwalker/alpine-elixir:1.10.3
ARG VERSION=0.7.0
  
WORKDIR /app
COPY --from=builder /app/_build/prod/rel/app /app/
    
RUN ln -s ./app/lib/app_nameapi_web-${VERSION}/priv/static /app/static
ENV HOME=/app
  
EXPOSE 4000    
  
ENTRYPOINT ["./bin/app", "start"]

and then the docker-compose

services:
    app:
    image: eu-west-3.amazonaws.com/app/web:staging
    expose:       
      - "4000/tcp"             
    environment:               
      - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
      - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
      - AWS_BUCKET_NAME=${AWS_BUCKET_NAME}
      - DATABASE_URL=${DATABASE_URL}  
      - SECRET_KEY_BASE=${SECRET_KEY_BASE}
    env_file:
      - envfile
    volumes:
      - appweb_static_vol:/app/static

Any direction on where to look for the problem would be greatly appriciated !

Don’t know if it’s a typo but there’s an additional _IE in your elixir code.

Yes, it’s just an example the variable names are different.

I don’t recall how RUN works in docker but since that @module_attribute is being set at compilation time you need to make sure it’s available when you do mix compile/release

1 Like

So for example I can export them before issuing the docker build command?

You need to make sure that when running the task for building the release or compiling the app those values are accessible in the task.

2 Likes

That was exactly correct thank you for that really usefull comment.

You are setting the @bucket module attribute with whatever the AWS_BUCKET_NAME_IE contains during compiletime.

Please use config/release.exs or config/runtime.exs depending on your elixir version to configure the application environment and read it during runtime.

3 Likes

I’m not sure if “compile time configuration” is the proper way to go.

It requires a full rebuild including redeploy if the setting has to be changed.

1 Like

I usually prefer env vars readable at runtime too but being a docker container might also mean that you’re not changing them while it’s running.

True but for me usually compile time configuration for the most part is in files that I would want to keep in the revision control system so not the best part to keep secrets. Also what is the mechanism of elixir/phoenix to read enviroment variables during runtime ?

I have only used docker to generate releases and I basically used those releases in systemd, using the service definition to pass in env/env files to the release and from elixir just doing regular System.get_env. The only thing you can’t do in that case is to make module attributes out of the get_env calls as those will be baked during generation.

I think you can now do better with what @NobbZ mentioned, maybe this helps Configuration and releases - The Elixir programming language

1 Like