No description
  • Shell 86.1%
  • Dockerfile 13.9%
Find a file
opgl fe57501e75
All checks were successful
Build and Push — vintage-story / docker (push) Successful in 5s
fix: NullReferenceException at startup
2026-07-02 12:29:48 +02:00
.forgejo/workflows first commit 2026-05-25 10:03:48 +00:00
docs fix: permissions 2026-05-25 10:44:57 +00:00
garrysmod fix: chown /data before mkdir (CAP_CHOWN works without DAC_OVERRIDE) 2026-06-22 12:37:08 +02:00
hytale fix: permissions 2026-05-25 10:44:57 +00:00
minecraft fix: chown /data before mkdir (CAP_CHOWN works without DAC_OVERRIDE) 2026-06-22 12:37:08 +02:00
vintage-story fix: NullReferenceException at startup 2026-07-02 12:29:48 +02:00
README.md fix: permissions 2026-05-25 10:44:57 +00:00

ShardHost — Game Server Docker Images

Docker images designed to work with the ShardHost game hosting platform (shard-rs + server-rs). Each image acts as a self-contained game server runner that:

  • Downloads and installs the game server on first start
  • Stores all persistent data in /data (mounted by shard-rs)
  • Accepts configuration via environment variables
  • Keeps stdin open for console command input
  • Handles graceful shutdown via SIGTERM

Architecture

shard-rs  ─────────────────────────────────────────────────────────────────────
  │
  │  mounts  {data_dir}/{container_name}  →  /data  (inside container)
  │  SFTP chroot also rooted at the same /data directory
  │
  └─► Docker container
        ├── /data/          ← persistent volume (game files, saves, logs, config)
        └── /               ← read-only image layer (binaries, entrypoint)
Concern How it is handled
Persistent data shard-rs mounts {data_dir}/{container_name} as /data inside the container. All game files, saves, and logs live here.
SFTP access Users are chrooted to the same /data directory — they see exactly what the game server sees.
Resource limits CPU, memory, and disk quotas are enforced at the Docker container level by shard-rs.
Container security no-new-privileges flag set; capabilities dropped to the minimum required (CHOWN, NET_BIND_SERVICE, KILL). Game server processes run as uid 1000 (gameserver).

Available Images

Image Game Default Port Base Image
git.plochit.pl/shardhost.eu/minecraft Minecraft Java Edition 25565 TCP/UDP eclipse-temurin:21-jre-alpine
git.plochit.pl/shardhost.eu/vintage-story Vintage Story 42420 TCP/UDP dotnet/runtime:8.0-bookworm-slim
git.plochit.pl/shardhost.eu/garrysmod Garry's Mod 27015 TCP/UDP debian:bookworm-slim
git.plochit.pl/shardhost.eu/hytale Hytale (placeholder) 25565 TCP alpine:3.20

Configuration in server-rs

Each game requires a RuntimeConfig row in the database. The docker_image field identifies which image to run; env_vars (JSON object) are injected as container environment variables.

Minecraft

INSERT INTO runtime_configs (name, docker_image, env_vars, internal_port, startup_command, description)
VALUES (
  'Minecraft Java (Paper)',
  'git.plochit.pl/shardhost.eu/minecraft:latest',
  '{"MC_TYPE":"paper","MC_VERSION":"latest","EULA":"true","MEMORY":"max","ONLINE_MODE":"true"}',
  25565,
  '',
  'Minecraft Java Edition — PaperMC'
);

Vintage Story

INSERT INTO runtime_configs (name, docker_image, env_vars, internal_port, startup_command, description)
VALUES (
  'Vintage Story',
  'git.plochit.pl/shardhost.eu/vintage-story:latest',
  '{"VS_VERSION":"latest","ADVERTISE":"false"}',
  42420,
  '',
  'Vintage Story dedicated server'
);

Garry's Mod

INSERT INTO runtime_configs (name, docker_image, env_vars, internal_port, startup_command, description)
VALUES (
  'Garry''s Mod',
  'git.plochit.pl/shardhost.eu/garrysmod:latest',
  '{"GM_GAMEMODE":"sandbox","GM_MAP":"gm_flatgrass","TICKRATE":"66"}',
  27015,
  '',
  'Garry''s Mod dedicated server (SteamCMD)'
);

Environment Variables

Minecraft (git.plochit.pl/shardhost.eu/minecraft)

Variable Default Description
MC_VERSION latest Server version (e.g. 1.21.4)
MC_TYPE paper Server type: paper, vanilla, purpur
SERVER_PORT 25565 Game port (TCP+UDP)
EULA false Must be true to start — accepts the Mojang EULA
MEMORY 1024 JVM heap in MB (recommended: ~85% of container memory limit)
MAX_PLAYERS 20 Maximum simultaneous players
DIFFICULTY normal peaceful / easy / normal / hard
GAMEMODE survival survival / creative / adventure / spectator
MOTD A Minecraft Server Description shown in the server list
ONLINE_MODE true Require premium (Mojang/Microsoft) authentication
EXTRA_FLAGS (empty) Additional JVM arguments appended to the launch command

Vintage Story (git.plochit.pl/shardhost.eu/vintage-story)

Variable Default Description
VS_VERSION latest Server version (e.g. 1.20.0)
SERVER_PORT 42420 Game port (TCP+UDP)
MAX_CLIENTS 16 Maximum simultaneous players
SERVER_NAME Vintage Story Server Server display name
WORLD_NAME default World / save name
SAVE_BACKUPS 3 Number of rolling backup saves to keep
ADVERTISE false List the server on the public server list
MOTD (empty) Server description shown on join

Garry's Mod (git.plochit.pl/shardhost.eu/garrysmod)

Variable Default Description
SERVER_PORT 27015 Game port (TCP+UDP)
GM_MAP gm_flatgrass Starting map
GM_GAMEMODE sandbox Default gamemode
GM_MAXPLAYERS 16 Maximum simultaneous players
STEAM_TOKEN (empty) GSL token — server is LAN-only without one
SERVER_NAME Garry's Mod Server Server hostname (visible in browser)
SERVER_PASS (empty) Join password (empty = no password)
TICKRATE 66 Server tickrate
EXTRA_ARGS (empty) Additional srcds arguments

Data Directory Layout

The /data volume is what users see when they connect over SFTP. Its structure varies per game:

/data/                          ← SFTP root (chrooted here)
│
├── [minecraft]
│   ├── server.jar              ← Downloaded server binary
│   ├── eula.txt
│   ├── server.properties       ← Edit to customise beyond env vars
│   ├── world/                  ← World data
│   ├── plugins/                ← Paper/Purpur plugins
│   └── logs/
│
├── [vintage-story]
│   ├── server/                 ← VS server binary (auto-managed, do not edit)
│   ├── serverconfig.json       ← Regenerated from env vars on each start
│   └── Data/
│       ├── Saves/
│       ├── Logs/
│       └── ModConfig/
│
└── [garrysmod]
    └── server/                 ← Full game install (auto-updated via SteamCMD)
        └── garrysmod/
            ├── cfg/
            │   └── server.cfg  ← Regenerated from env vars on each start
            ├── addons/         ← Install Workshop add-ons here
            └── maps/

Building

# Build all images
docker build -t git.plochit.pl/shardhost.eu/minecraft:latest       ./minecraft
docker build -t git.plochit.pl/shardhost.eu/vintage-story:latest   ./vintage-story
docker build -t git.plochit.pl/shardhost.eu/garrysmod:latest       ./garrysmod
docker build -t git.plochit.pl/shardhost.eu/hytale:latest          ./hytale

Notes

  • Garry's Mod / SteamCMD — the game server is updated on every container start. Keep /data/server/ on a persistent volume to avoid re-downloading the full game (~8 GB) each time.

  • Minecraft server.properties — env vars update known keys on each start, but the file is never recreated from scratch. Custom properties added manually are preserved across restarts.

  • Vintage Story serverconfig.json — this file is fully regenerated from env vars on every start. To persist custom settings, use the Data/ subdirectory for mods and configuration that VS reads separately.

  • User security — all images run the game server process as uid 1000 (gameserver). The container itself runs with no-new-privileges and a reduced Linux capability set.

  • Hytale placeholder — the git.plochit.pl/shardhost.eu/hytale image keeps the container alive and records a log entry at /data/logs/shardhost.log. It will be replaced with a real implementation once the game releases.