Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions .github/workflows/docker-publish.yml

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you want to push dev images? I would drop the entire thing

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for bar-lobby so you can run one command and have a working setup.

Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Docker publish

on:
push:
branches:
- master
workflow_dispatch:

permissions:
contents: read
packages: write

jobs:
build-and-push:
runs-on: ubuntu-latest
name: Build and push Docker images

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Determine tags
id: tags
run: |
DEFAULT_BRANCH="${{ github.event.repository.default_branch }}"
CURRENT_BRANCH="${{ github.ref_name }}"

if [ "$CURRENT_BRANCH" = "$DEFAULT_BRANCH" ]; then
NUMBER="${{ github.run_number }}"
else
NUMBER="$(git rev-parse --short=10 HEAD)"
fi
REPOSITORY="${{ github.repository }}"
REGISTRY="ghcr.io/${REPOSITORY,,,}"

if [ "$CURRENT_BRANCH" = "$DEFAULT_BRANCH" ]; then
echo "release_tags=${REGISTRY}:build-${NUMBER}-release,${REGISTRY}:latest-release" >> "$GITHUB_OUTPUT"
echo "dev_tags=${REGISTRY}:build-${NUMBER}-dev,${REGISTRY}:latest-dev" >> "$GITHUB_OUTPUT"
else
echo "release_tags=${REGISTRY}:git-${NUMBER}-release" >> "$GITHUB_OUTPUT"
echo "dev_tags=${REGISTRY}:git-${NUMBER}-dev" >> "$GITHUB_OUTPUT"
fi

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's this github token? Where does that live?

@TimMeissner TimMeissner Feb 22, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is autopopulated by github in actions/workflows, no need to set it up in any way.


- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build and push release image
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile
target: runner
push: true
tags: ${{ steps.tags.outputs.release_tags }}
cache-from: type=gha,scope=docker
cache-to: type=gha,mode=max,scope=docker

- name: Build and push dev image
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile
target: dev
push: true
tags: ${{ steps.tags.outputs.dev_tags }}
cache-from: type=gha,scope=docker
cache-to: type=gha,mode=max,scope=docker
77 changes: 69 additions & 8 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
# This file is written in a way that tries to optimize caching of the build steps
# as much as possible. It is increasing the complexity of the file a bit.
#
# The image can be built with the following command from the teiserver repo root:
# The production image can be built with the following command from the teiserver repo root:
#
# sudo podman build -t teiserver -f .
# podman build --target runner -t teiserver -f .
#
# The release is copied and placed in the filesystem of the host, but it could be
# also run as a container with something like:
Expand All @@ -19,14 +19,19 @@
# -v /etc/ssl/dhparam.pem:/etc/ssl/dhparam.pem:ro \
# teiserver
#
# Development image (used by docker-compose.yml):
# podman build --target dev -t teiserver-dev .

ARG ELIXIR_VERSION=1.19.4
ARG OTP_VERSION=26.2.5.1
ARG DEBIAN_VERSION=trixie-20251208
ARG BUILDER_IMAGE="docker.io/hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}"
ARG RUNNER_IMAGE="docker.io/debian:${DEBIAN_VERSION}-slim"

FROM ${BUILDER_IMAGE} as builder
# ============================================================
# base — shared foundation for builder and dev stages
# ============================================================
FROM ${BUILDER_IMAGE} AS base

RUN apt-get update \
&& apt-get install --no-install-recommends --yes git make build-essential \
Expand All @@ -39,15 +44,20 @@ RUN mix local.hex --force \
RUN mkdir /build
WORKDIR /build

ENV MIX_ENV="prod"

COPY mix.exs mix.lock ./
RUN mix deps.get

# ============================================================
# builder — compiles and assembles the production release
# ============================================================
FROM base AS builder

ENV MIX_ENV="prod"

RUN mkdir config
COPY config/config.exs config/

# Need to also compiled dev to be able to compile static assets...
# Need to also compile dev to be able to compile static assets...
COPY config/dev.exs config/
RUN MIX_ENV=dev mix deps.compile

Expand All @@ -70,7 +80,58 @@ COPY config/runtime.exs config/
COPY rel rel
RUN mix release

FROM ${RUNNER_IMAGE}
# ============================================================
# dev — hot-reload development container
# ============================================================
FROM base AS dev

RUN apt-get update \
&& apt-get install --no-install-recommends --yes \
curl \
inotify-tools \
postgresql-client \
geoip-bin \
geoip-database \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

ENV MIX_ENV=dev

RUN mix deps.compile

# dart_sass downloads a standalone Dart Sass binary on first use; install it
# here so it is baked into the image rather than fetched at container startup.
RUN mix sass.install

COPY mise.toml /build/
COPY *.exs /build/
COPY *.json /build/
COPY *.yaml /build/

COPY assets /build/assets
COPY bin /build/bin
COPY config /build/config
COPY lib /build/lib
COPY misc /build/misc
COPY priv /build/priv
COPY rel /build/rel
COPY scripts /build/scripts
COPY test /build/test

RUN mix credo --strict || true
RUN mix format
RUN mix compile

COPY docker/teiserver/docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh

ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["iex", "-S", "mix", "phx.server"]

# ============================================================
# runner — minimal production runtime image, last stage is the default output
# ============================================================
FROM ${RUNNER_IMAGE} AS runner

RUN apt-get update \
&& apt-get install --no-install-recommends --yes libstdc++6 openssl libncurses6 locales tini \
Expand All @@ -85,7 +146,7 @@ ENV LC_ALL en_US.UTF-8
WORKDIR "/app"
ENV MIX_ENV="prod"

COPY --from=builder /build/_build/${MIX_ENV}/rel/teiserver ./
COPY --from=builder /build/_build/${MIX_ENV}/rel/teiserver ./

ENTRYPOINT ["tini", "--"]
CMD /app/bin/teiserver start
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,24 @@ There are two ways to set up Teiserver locally for development or testing:
1. The [Local setup](/documents/guides/local_setup.md) guides you through the process of setting up everything yourself
2. The [Local testing](https://github.com/beyond-all-reason/ansible-teiserver?tab=readme-ov-file#local-testing) instructions use the Ansible playbook, which automates most of the setup and configuration.

## Compose dev setup
For a quick local stack (including teiserver with hot-reload) use the compose setup in this repo:

```sh
docker compose -f docker-compose.dev.yml up --build
```

To start only Postgres for local Teiserver development:

```sh
docker compose -f docker-compose.dev.yml up -d db
```

### Windows performance note
On Windows, bind mounts can make Teiserver startup and runtime noticeably slower (file watcher and filesystem overhead).

If this bothers you, comment out the source-code mount block for `teiserver` in `docker-compose.dev.yml` (the `volumes` entries under the `teiserver` service), then rebuild/restart the stack.

## Prod setup
Production instance is set up using [Ansible playbook](https://github.com/beyond-all-reason/ansible-teiserver/tree/main), follow the setup instructions there.

Expand Down
13 changes: 6 additions & 7 deletions config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -195,11 +195,10 @@ if Teiserver.ConfigHelpers.get_env("TEI_ENABLE_EMAIL_INTEGRATION", false, :bool)
hostname: Teiserver.ConfigHelpers.get_env("TEI_SMTP_HOSTNAME"),
# port: 1025,
port: Teiserver.ConfigHelpers.get_env("TEI_SMTP_PORT", "587", :int),
username: Teiserver.ConfigHelpers.get_env("TEI_SMTP_USERNAME"),
password: Teiserver.ConfigHelpers.get_env("TEI_SMTP_PASSWORD"),
# tls: :if_available, # can be `:always` or `:never`
# can be `:always` or `:never`
tls: :always,
username: Teiserver.ConfigHelpers.get_env("TEI_SMTP_USERNAME", ""),
password: Teiserver.ConfigHelpers.get_env("TEI_SMTP_PASSWORD", ""),
# can be `:always`, `:never`, or `:if_available`
tls: Teiserver.ConfigHelpers.get_env("TEI_SMTP_TLS", "always") |> String.to_existing_atom(),
tls_verify:
if(Teiserver.ConfigHelpers.get_env("TEI_SMTP_TLS_VERIFY", true, :bool),
do: :verify_peer,
Expand All @@ -209,8 +208,8 @@ if Teiserver.ConfigHelpers.get_env("TEI_ENABLE_EMAIL_INTEGRATION", false, :bool)
allowed_tls_versions: [:"tlsv1.2"],
# can be `true`
no_mx_lookups: false,
# auth: :if_available # can be `always`. If your smtp relay requires authentication set it to `always`.
auth: :always
# can be `:always`, `:if_available`, or `:never`
auth: Teiserver.ConfigHelpers.get_env("TEI_SMTP_AUTH", "always") |> String.to_existing_atom()
end

log_root_path = Teiserver.ConfigHelpers.get_env("TEI_LOG_ROOT_PATH", "/tmp/teiserver")
Expand Down
115 changes: 115 additions & 0 deletions docker-compose.yml

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing vmagent, to actually scrape and store the metrics in victoriametrics.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is using the built in scraper from victoriametrics (check line 40 and the prometheus.yml file), i can use the metrics in grafana just fine

Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
name: teiserver-dev
services:
db:
image: postgres:18
restart: unless-stopped
environment:
POSTGRES_PASSWORD: 123456789
ports:
- "5432:5432"
volumes:
- teiserver_postgres_data:/var/lib/postgresql
- ./docker/postgres/init:/docker-entrypoint-initdb.d:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 20

mailhog:
image: mailhog/mailhog:v1.0.1
restart: unless-stopped
ports:
- "1025:1025" # SMTP
- "8025:8025" # Web UI

victoriametrics:
image: victoriametrics/victoria-metrics:v1.136.0
restart: unless-stopped
ports:
- "8428:8428"
volumes:
- teiserver_victoriametrics_data:/victoria-metrics-data
- ./docker/victoriametrics/prometheus.yml:/etc/prometheus/prometheus.yml:ro
command:
- "-retentionPeriod=14d"
- "-httpListenAddr=:8428"
- "-promscrape.config=/etc/prometheus/prometheus.yml"

grafana:
image: grafana/grafana:main
container_name: teiserver-grafana
restart: unless-stopped
depends_on:
- victoriametrics
environment:
GF_SECURITY_ADMIN_USER: admin
GF_SECURITY_ADMIN_PASSWORD: admin
GF_USERS_ALLOW_SIGN_UP: "false"
GF_INSTALL_PLUGINS: ""
ports:
- "3000:3000"
volumes:
- teiserver_grafana_data:/var/lib/grafana
- ./docker/grafana/provisioning:/etc/grafana/provisioning:ro

teiserver:
build:
context: .
dockerfile: Dockerfile
target: dev
container_name: teiserver
depends_on:
db:
condition: service_healthy
environment:
# Database
TEI_DB_HOSTNAME: "db"
TEI_DB_USERNAME: "teiserver_dev"
TEI_DB_PASSWORD: "123456789"
TEI_DB_NAME: "teiserver_dev"

# Application
SECRET_KEY_BASE: "dev_secret_key_base_change_in_production_min_64_chars_long_string"
TEI_OAUTH_ISSUER: ${TEI_OAUTH_ISSUER:-http://localhost:4000}
TEI_DOMAIN_NAME: ${TEI_DOMAIN_NAME:-localhost}

# Email integration (MailHog)
TEI_ENABLE_EMAIL_INTEGRATION: "true"
TEI_SMTP_SERVER: "mailhog"
TEI_SMTP_HOSTNAME: "localhost"
TEI_SMTP_PORT: "1025"
TEI_SMTP_TLS: "never"
TEI_SMTP_TLS_VERIFY: "false"
TEI_SMTP_AUTH: "never"

# Dev settings
GENERATE_FAKE_DATA: "true"
IN_DOCKER: "true"
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:4000 || exit 1"]
interval: 10s
timeout: 5s
retries: 10
start_period: 90s
ports:
- "4000:4000" # HTTP
- "4001:4001" # HTTP Metrics
- "8200:8200" # Spring protocol
- "8201:8201" # Spring protocol
- "8202:8202" # Spring protocol
volumes:
# Mount source code for live reload, might cause issues on Windows.
# If you run into performance issues, remove these mounts.
- ./lib:/build/lib
- ./config:/build/config
- ./priv:/build/priv
- ./assets:/build/assets
- ./test:/build/test
stdin_open: true
tty: true

volumes:
teiserver_postgres_data:
teiserver_victoriametrics_data:
teiserver_grafana_data:
10 changes: 10 additions & 0 deletions docker/grafana/provisioning/datasources/datasource.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: 1

datasources:
- name: VictoriaMetrics
type: prometheus
access: proxy
url: http://victoriametrics:8428
isDefault: true
editable: true

5 changes: 5 additions & 0 deletions docker/postgres/init/01-init-teiserver.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATE ROLE teiserver_dev LOGIN PASSWORD '123456789' SUPERUSER;
CREATE ROLE teiserver_test LOGIN PASSWORD '123456789' SUPERUSER;

CREATE DATABASE teiserver_dev OWNER teiserver_dev;
CREATE DATABASE teiserver_test OWNER teiserver_test;
Loading
Loading