RicoTrevisan

RicoTrevisan

PhoenixAnalytics: Permission denied error when accessing DuckDB file in Docker/Coolify deployment

I’m trying to add PhoenixAnalytics by @lalabuy948 to a Phoenix project. I got it to work locally, but I can’t make it work on my Hetzner server. My guess is that I’ve got to change my Dockerfile but I’ve gone back and forth with the LLMs and I got nowhere. Hoping anyone can point me to some more troubleshooting ideas.

Let me start from the end.
In the application logs, here’s the error:

05:45:14.329 request_id=F__EY628KPIaoG4AAABD [info] GET /api/v1/
05:45:14.329 request_id=F__EY628KPIaoG4AAABD [info] Sent 200 in 481µs
05:45:15.206 [error] GenServer PhoenixAnalytics.Services.Batcher terminating
** (MatchError) no match of right hand side value: {:error, "Table 'requests' could not be found"}
    (phoenix_analytics 0.2.0) lib/phoenix_analytics/repo.ex:215: PhoenixAnalytics.Repo.insert_many/1
    (phoenix_analytics 0.2.0) lib/phoenix_analytics/services/batcher.ex:101: PhoenixAnalytics.Services.Batcher.handle_info/2
    (stdlib 6.0) gen_server.erl:2173: :gen_server.try_handle_info/3
    (stdlib 6.0) gen_server.erl:2261: :gen_server.handle_msg/6
    (stdlib 6.0) proc_lib.erl:329: :proc_lib.init_p_do_apply/3
Last message: :check_batch

Coolify setup

Persistent storage

Host Server

I can see that directory if I hop on the host server

inside the Docker container

Environment Variables

I add DUCKDB_PATH as an env variable

/app/data/analytics.duckdb

Elixir setup

config.exs

Here’s the bit I added

config :phoenix_analytics,
  duckdb_path: System.get_env("DUCKDB_PATH") || "/app/data/analytics.duckdb",
  app_domain: System.get_env("PHX_HOST") || "reabra.com.br"

endpoint.ex

I add the following

  plug Plug.Static,
    at: "/",
    from: :reabra,
    gzip: false,
    only: ReabraWeb.static_paths()

  plug PhoenixAnalytics.Plugs.RequestTracker

...

router.ex

...
  use PhoenixAnalytics.Web, :router
...
  scope "/admin", ReabraWeb do
    pipe_through [:browser, :require_authenticated_user, :require_admin]
    phoenix_analytics_dashboard("/analytics")

...

Dockerfile

Here’s my Dockerfile

ARG ELIXIR_VERSION=1.17.2
ARG OTP_VERSION=27.0
ARG DEBIAN_VERSION=bullseye-20240701-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

# 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}
ARG DUCKDB_PATH

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

# Install DuckDB
RUN wget https://github.com/duckdb/duckdb/releases/download/v1.1.2/duckdb_cli-linux-amd64.zip \
    && unzip duckdb_cli-linux-amd64.zip \
    && mv duckdb /usr/local/bin/ \
    && rm duckdb_cli-linux-amd64.zip

# 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
ENV DUCKDB_PATH=${DUCKDB_PATH}

WORKDIR "/app"
RUN mkdir -p /app/data && chown -R nobody:root /app/data
RUN touch /app/data/analytics.duckdb && chown nobody:root /app/data/analytics.duckdb
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/reabra ./

USER nobody

# If using an environment that doesn't automatically reap zombie processes, it is
# advised to add an init process such as tini via `apt-get install`
# above and adding an entrypoint. See https://github.com/krallin/tini for details
# ENTRYPOINT ["/tini", "--"]

# CMD ["/app/bin/server"]
CMD ["sh", "-c", "/app/bin/migrate && /app/bin/server"]

Migration

I added the necessary migration

defmodule Reabra.Repo.Migrations.AddPhoenixAnalytics do
  use Ecto.Migration

  def up, do: PhoenixAnalytics.Migration.up()
  def down, do: PhoenixAnalytics.Migration.down()
end


FYI: There’s a nice walkthrough / demo here:

Most Liked

ruslandoga

ruslandoga

It might be a bit off-topic, but I had a similar problem with SQLite in the past. For me it was that the owner of the bind mount was different from UID in the container. I think this is also the problem here as /app/data seems to be owned by 1001 (probably host’s reabra) and not nobody.

Also I think bind mounts would replace the contents of the container image. So

RUN mkdir -p /app/data && chown -R nobody:root /app/data
RUN touch /app/data/analytics.duckdb && chown nobody:root /app/data/analytics.duckdb

might be deleted and replaced with the hosts /data/coolify/…/…/data contents (which is owned by 1001 and not nobody).

I know of two easy fixes:

  • allow hosts’s /data/coolify/…/…/data to be read and written by UID of the container
  • or use volumes :slight_smile:
lalabuy948

lalabuy948

Hi @RicoTrevisan, it looks like you didn’t run migration.
As if PhoenixAnalytics would not have access file to write it would not start at all.

You can try to run it manually:

iex -S mix 
PhoenixAnalytics.Migration.up()

Where Next?

Popular in Questions Top

dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New

Other popular topics Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 45766 226
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
AstonJ
by Lance Halvorsen Elixir and Phoenix are generating tremendous excitement as an unbeatable platform for building modern web application...
460 27162 124
New

We're in Beta

About us Mission Statement