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

chokchit
** (DBConnection.ConnectionError) connection not available and request was dropped from queue after 2733ms. You can configure how long re...
New
marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
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
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call t...
New

Other popular topics Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 42920 311
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
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 47930 226
New

We're in Beta

About us Mission Statement