Open source game server with authentication, users, lobbies, server scripting and an admin portal.
Game + Backend = Gamend
Discord | Elixir Docs | API Docs | Guides | Starter Template | Architecture | Deployment Tutorial | Scaling Article
To start your server:
- Run
mix setupto install and setup dependencies.
Now you can visit localhost:4000 from your browser.
This application supports two authentication methods:
Traditional session-based authentication for browser flows:
- Email/password registration and login
- Discord OAuth
- Apple Sign In
- Google OAuth
- Steam OpenID
- Facebook OAuth
- Session tokens stored in database
- Managed via cookies and Phoenix sessions
Modern JWT authentication using access + refresh tokens (industry standard):
Token Types:
- Access tokens: Short-lived (15 minutes), used for API requests
- Refresh tokens: Long-lived (30 days), used to obtain new access tokens
User management:
- Multiple sign-in flows supported: Email/password, device tokens (SDK), and OAuth (Discord / Google / Facebook / Apple).
- Per-user profile metadata as JSON
- Account lifecycle: registration, login, password reset, and account deletion endpoints.
Social features:
- Friend requests with accept / reject / block flows.
Matchmaking and lobbies:
- Host-managed behavior, max users, hidden/locked states, and password protection.
- Public APIs are provided for listing, creating, joining, leaving, updating and kicking.
Extendable server behavior:
- Hooks on server events (eg. on_user_login, on_lobby_created)
The sdk/ folder contains stub modules that provide IDE autocomplete and documentation for custom hook scripts. When building your own Starter Template, add the SDK as a dependency:
# mix.exs
defp deps do
[
{:game_server_sdk, github: "appsinacup/game_server", sparse: "sdk"}
]
endExample hook with autocomplete:
defmodule MyApp.Hooks do
use GameServer.Hooks
@impl true
def after_user_login(user) do
# IDE autocomplete works for user fields and Accounts functions
GameServer.Accounts.update_user(user, %{
metadata: Map.put(user.metadata, "last_login", DateTime.utc_now())
})
:ok
end
endTo regenerate SDK stubs from the main project:
mix gen.sdkHow to deploy (Starter Template)
- Fork this repo (or create a Dockerfile like this):
FROM ghcr.io/appsinacup/game_server:latest
WORKDIR /app
COPY modules/ ./modules/
COPY apps/game_server_web/priv/static/assets/css/theme/ ./apps/game_server_web/priv/static/assets/css/theme/
COPY apps/game_server_web/priv/static/images/ ./apps/game_server_web/priv/static/images/
# Build any plugins shipped in this repo (overlay) so they're available at runtime.
ARG GAME_SERVER_PLUGINS_DIR=modules/plugins
ENV GAME_SERVER_PLUGINS_DIR=${GAME_SERVER_PLUGINS_DIR}
RUN if [ -d "${GAME_SERVER_PLUGINS_DIR}" ]; then \
for plugin_path in ${GAME_SERVER_PLUGINS_DIR}/*; do \
if [ -d "${plugin_path}" ] && [ -f "${plugin_path}/mix.exs" ]; then \
echo "Building plugin ${plugin_path}"; \
(cd "${plugin_path}" && mix deps.get && mix compile && mix plugin.bundle); \
fi; \
done; \
else \
echo "Plugin sources dir ${GAME_SERVER_PLUGINS_DIR} missing, skipping plugin builds"; \
fi- Go to fly.io and deploy (select the forked repo).
- Set secrets all values in
.env.example. Run locallyfly secrets syncandfly secrets deploy(in case secrets don't deploy/update). - Configure all things from Guides page.
- Monthly cost (without Postgres) will be about 5$.
To run locally using Elixir:
-
Configure the
.envfile (copy.env.exampleto.env). -
Then run:
./start.shTo run with single instance, run:
docker compose upTo run multi instance with 2 instances, nginx load balancer, PostgreSQL database, Redis cache, run:
docker compose -f docker-compose.multi.yml up --scale app=2To install precommit hooks, run:
bin/setup-git-hooksTo skip, run:
SKIP_PRECOMMIT=1 git commit