From 324c5c556c94e444789d86a9080a2ef2cdc19d46 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Wed, 11 Mar 2026 16:51:44 -0500 Subject: [PATCH] Add Dokku deployment configuration --- .dockerignore | 26 ++++++++++ .tool-versions | 3 ++ Dockerfile | 95 ++++++++++++++++++++++++++++++++++++ Procfile | 1 + app.json | 7 +++ config/runtime.exs | 4 ++ lib/ammoprices/release.ex | 30 ++++++++++++ rel/overlays/bin/migrate | 5 ++ rel/overlays/bin/migrate.bat | 1 + rel/overlays/bin/server | 5 ++ rel/overlays/bin/server.bat | 2 + 11 files changed, 179 insertions(+) create mode 100644 .dockerignore create mode 100644 .tool-versions create mode 100644 Dockerfile create mode 100644 Procfile create mode 100644 app.json create mode 100644 lib/ammoprices/release.ex create mode 100644 rel/overlays/bin/migrate create mode 100644 rel/overlays/bin/migrate.bat create mode 100644 rel/overlays/bin/server create mode 100644 rel/overlays/bin/server.bat diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..dd59f17 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,26 @@ +.dockerignore + +# Ignore git, but keep git HEAD and refs to access current commit hash if needed +.git +!.git/HEAD +!.git/refs + +# Common development/test artifacts +/cover/ +/doc/ +/test/ +/tmp/ +.elixir_ls + +# Mix artifacts +/_build/ +/deps/ +*.ez + +# Generated on crash by the VM +erl_crash.dump + +# Static artifacts - These should be fetched and built inside the Docker image +/assets/node_modules/ +/priv/static/assets/ +/priv/static/cache_manifest.json diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..f5a1656 --- /dev/null +++ b/.tool-versions @@ -0,0 +1,3 @@ +erlang 28.3 +elixir 1.19.5-otp-28 +nodejs 22.17.1 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7bb4b07 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,95 @@ +# syntax=docker/dockerfile:1.4 + +# Find eligible builder and runner images on Docker Hub. +# +# - https://hub.docker.com/r/hexpm/elixir/tags - for the build image +# - https://hub.docker.com/_/debian/tags?name=trixie-20251229-slim - for the release image +# - Ex: docker.io/hexpm/elixir:1.19.5-erlang-28.3-debian-trixie-20251229-slim +# +ARG ELIXIR_VERSION=1.19.5 +ARG OTP_VERSION=28.3 +ARG DEBIAN_VERSION=trixie-20251229-slim + +ARG BUILDER_IMAGE="docker.io/hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}" +ARG RUNNER_IMAGE="docker.io/debian:${DEBIAN_VERSION}" + +FROM ${BUILDER_IMAGE} AS builder + +# install build dependencies +RUN apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends build-essential git \ + && rm -rf /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 --mount=type=cache,target=/root/.hex \ + --mount=type=cache,target=/root/.mix \ + 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 --mount=type=cache,target=/root/.hex \ + --mount=type=cache,target=/root/.mix \ + mix deps.compile + +RUN --mount=type=cache,target=/root/.mix \ + mix assets.setup + +COPY priv priv +COPY lib lib + +# Compile the release +RUN mix compile + +COPY assets assets + +# compile assets +RUN mix assets.deploy + +# 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} AS final + +RUN apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends libstdc++6 openssl libncurses6 locales ca-certificates \ + && rm -rf /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/ammoprices ./ + +USER nobody + +CMD ["/app/bin/server"] diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..b6bc2a8 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: /app/bin/server diff --git a/app.json b/app.json new file mode 100644 index 0000000..441a39d --- /dev/null +++ b/app.json @@ -0,0 +1,7 @@ +{ + "scripts": { + "dokku": { + "predeploy": "/app/bin/migrate" + } + } +} diff --git a/config/runtime.exs b/config/runtime.exs index 74ef18b..0f0a738 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -56,6 +56,10 @@ if config_env() == :prod do config :ammoprices, AmmopricesWeb.Endpoint, url: [host: host, port: 443, scheme: "https"], + check_origin: [ + "https://#{host}", + "https://www.#{host}" + ], http: [ # Enable IPv6 and bind on all interfaces. # Set it to {0, 0, 0, 0, 0, 0, 0, 1} for local network only access. diff --git a/lib/ammoprices/release.ex b/lib/ammoprices/release.ex new file mode 100644 index 0000000..d4e1ea5 --- /dev/null +++ b/lib/ammoprices/release.ex @@ -0,0 +1,30 @@ +defmodule Ammoprices.Release do + @moduledoc """ + Used for executing DB release tasks when run in production without Mix + installed. + """ + @app :ammoprices + + def migrate do + load_app() + + for repo <- repos() do + {:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true)) + end + end + + def rollback(repo, version) do + load_app() + {:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, to: version)) + end + + defp repos do + Application.fetch_env!(@app, :ecto_repos) + end + + defp load_app do + # Many platforms require SSL when connecting to the database + Application.ensure_all_started(:ssl) + Application.ensure_loaded(@app) + end +end diff --git a/rel/overlays/bin/migrate b/rel/overlays/bin/migrate new file mode 100644 index 0000000..45206a7 --- /dev/null +++ b/rel/overlays/bin/migrate @@ -0,0 +1,5 @@ +#!/bin/sh +set -eu + +cd -P -- "$(dirname -- "$0")" +exec ./ammoprices eval Ammoprices.Release.migrate diff --git a/rel/overlays/bin/migrate.bat b/rel/overlays/bin/migrate.bat new file mode 100644 index 0000000..71f06b9 --- /dev/null +++ b/rel/overlays/bin/migrate.bat @@ -0,0 +1 @@ +call "%~dp0\ammoprices" eval Ammoprices.Release.migrate diff --git a/rel/overlays/bin/server b/rel/overlays/bin/server new file mode 100644 index 0000000..23f3da8 --- /dev/null +++ b/rel/overlays/bin/server @@ -0,0 +1,5 @@ +#!/bin/sh +set -eu + +cd -P -- "$(dirname -- "$0")" +PHX_SERVER=true exec ./ammoprices start diff --git a/rel/overlays/bin/server.bat b/rel/overlays/bin/server.bat new file mode 100644 index 0000000..f431f81 --- /dev/null +++ b/rel/overlays/bin/server.bat @@ -0,0 +1,2 @@ +set PHX_SERVER=true +call "%~dp0\ammoprices" start