Proof of concept: Container management with FastAPI backend

- FastAPI backend with REST API endpoints
- SQLite database for container metadata
- Docker/Podman SDK integration with label filtering
- Frontend: Server creation form and management page
- Container operations: create, list, start, stop, delete
- Single container deployment (nginx + Python + supervisor)
- Support for Docker and Podman (rootless)
- Volume management for persistent data
This commit is contained in:
robojerk 2025-10-31 11:50:31 -07:00
commit c31e48e2b1
25 changed files with 3382 additions and 0 deletions

7
.dockerignore Normal file
View file

@ -0,0 +1,7 @@
node_modules
.git
references
docs
*.md
!README.md

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
data/
*.db
__pycache__/
*.pyc
*.pyo
.env

71
Dockerfile Normal file
View file

@ -0,0 +1,71 @@
FROM python:3.11-slim
# Install nginx
RUN apt-get update && apt-get install -y nginx supervisor && \
rm -rf /var/lib/apt/lists/*
# Set working directory
WORKDIR /app
# Create data directory
RUN mkdir -p /app/data && chmod 777 /app/data
# Copy backend requirements and install
COPY backend/requirements.txt /app/backend/
RUN pip install --no-cache-dir -r backend/requirements.txt
# Copy backend code
COPY backend/ /app/backend/
# Copy frontend files
COPY index.html manage.html /usr/share/nginx/html/
# Configure nginx
RUN echo 'server {\n\
listen 80;\n\
server_name localhost;\n\
root /usr/share/nginx/html;\n\
index index.html;\n\
\n\
location / {\n\
try_files $uri $uri/ /index.html;\n\
}\n\
\n\
location /api {\n\
proxy_pass http://127.0.0.1:8000;\n\
proxy_set_header Host $host;\n\
proxy_set_header X-Real-IP $remote_addr;\n\
}\n\
}' > /etc/nginx/sites-available/default
# Configure supervisor
RUN echo '[supervisord]\n\
nodaemon=true\n\
logfile=/dev/stdout\n\
logfile_maxbytes=0\n\
pidfile=/var/run/supervisord.pid\n\
\n\
[program:nginx]\n\
command=nginx -g "daemon off;"\n\
autostart=true\n\
autorestart=true\n\
stdout_logfile=/dev/stdout\n\
stdout_logfile_maxbytes=0\n\
stderr_logfile=/dev/stderr\n\
stderr_logfile_maxbytes=0\n\
\n\
[program:fastapi]\n\
command=uvicorn backend.main:app --host 127.0.0.1 --port 8000\n\
directory=/app\n\
environment=PYTHONPATH=/app\n\
autostart=true\n\
autorestart=true\n\
stdout_logfile=/dev/stdout\n\
stdout_logfile_maxbytes=0\n\
stderr_logfile=/dev/stderr\n\
stderr_logfile_maxbytes=0\n\
' > /etc/supervisor/conf.d/supervisord.conf
EXPOSE 80
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]

57
README.md Normal file
View file

@ -0,0 +1,57 @@
# Stronghold
A web ui to manage minecraft server using the itzg docker container
https://github.com/itzg/docker-minecraft-server
Also manage proxy servers like velocity, bungeecord
## Development
See design.md for design notes
## Quick Start (Proof of Concept)
Stronghold is a web UI for managing Minecraft servers using Docker/Podman containers.
### Running the POC
**Using Docker Compose (Docker users)**
```bash
docker-compose up -d --build
```
**Using Podman Compose (Podman users)**
```bash
podman-compose up -d --build
```
**Note**: The container needs access to your Docker/Podman socket to manage containers. The compose file mounts:
- `/var/run/docker.sock` (Docker)
- `/run/podman/podman.sock` (Podman)
Then open http://localhost:8080 in your browser.
### Features
**Current POC Features:**
- ✅ Generate docker-compose.yml files
- ✅ Create Minecraft server containers directly
- ✅ List and manage containers
- ✅ Start/Stop/Delete containers
- ✅ Support for Docker and Podman
- ✅ SQLite database for container metadata
- ✅ Multiple server types (Vanilla, Paper, Forge, Fabric, etc.)
- ✅ Modpack support (CurseForge, Modrinth, URL, Local)
- ✅ Advanced configuration options
**Pages:**
- `/` (index.html) - Create new servers
- `/manage.html` - Manage existing servers
### Architecture
- **Frontend**: Static HTML/CSS/JavaScript
- **Backend**: Python FastAPI
- **Database**: SQLite (stored in `./data/stronghold.db`)
- **Container Engine**: Docker SDK (works with both Docker and Podman)
All containers created by Stronghold are labeled with `stronghold.managed=true` to isolate them from other containers.

37
backend/database.py Normal file
View file

@ -0,0 +1,37 @@
from sqlalchemy import create_engine, Column, String, Integer, Text, Boolean, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from datetime import datetime
Base = declarative_base()
class Container(Base):
__tablename__ = "containers"
id = Column(Integer, primary_key=True, index=True)
container_id = Column(String, unique=True, index=True) # Docker container ID
name = Column(String, unique=True, index=True)
image = Column(String)
status = Column(String) # running, stopped, etc.
config_json = Column(Text) # Full config as JSON string
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
import os
# Create engine at module level
# Use /app/data for database storage (mounted volume)
data_dir = '/app/data'
os.makedirs(data_dir, exist_ok=True)
db_path = os.path.join(data_dir, 'stronghold.db')
engine = create_engine(f"sqlite:///{db_path}")
Base.metadata.create_all(bind=engine)
def get_db():
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
db = SessionLocal()
try:
yield db
finally:
db.close()

116
backend/docker_manager.py Normal file
View file

@ -0,0 +1,116 @@
import docker
import os
import json
LABEL_MANAGED = "stronghold.managed"
LABEL_VALUE = "true"
def get_docker_client():
"""Get Docker client, automatically detecting Docker or Podman"""
try:
# Try Docker socket first
if os.path.exists('/var/run/docker.sock'):
return docker.DockerClient(base_url='unix://var/run/docker.sock')
# Try Podman rootful
if os.path.exists('/run/podman/podman.sock'):
return docker.DockerClient(base_url='unix://run/podman/podman.sock')
# Try Podman rootless
xdg_runtime = os.environ.get('XDG_RUNTIME_DIR', '')
podman_sock = f'{xdg_runtime}/podman/podman.sock'
if xdg_runtime and os.path.exists(podman_sock):
return docker.DockerClient(base_url=f'unix://{podman_sock}')
# Fallback to default
return docker.DockerClient(base_url='unix://var/run/docker.sock')
except Exception as e:
raise Exception(f"Failed to connect to Docker/Podman: {e}")
def list_containers(client):
"""List all Stronghold-managed containers"""
filters = {"label": f"{LABEL_MANAGED}={LABEL_VALUE}"}
return client.containers.list(all=True, filters=filters)
def create_container(client, name, config):
"""Create a new container with Stronghold label"""
# Build environment variables from config
environment = {
"EULA": "TRUE" if config.get("acceptEULA") else "FALSE",
"TYPE": config.get("serverType", "PAPER"),
}
# Add optional environment variables
if config.get("version") and config.get("version") != "LATEST":
environment["VERSION"] = config["version"]
if config.get("memory"):
environment["MEMORY"] = config["memory"]
if config.get("difficulty"):
environment["DIFFICULTY"] = config["difficulty"]
if config.get("gamemode"):
environment["MODE"] = config["gamemode"]
if config.get("levelType"):
environment["LEVEL_TYPE"] = config["levelType"]
if config.get("motd"):
environment["MOTD"] = config["motd"]
if config.get("maxPlayers"):
environment["MAX_PLAYERS"] = str(config["maxPlayers"])
if config.get("viewDistance"):
environment["VIEW_DISTANCE"] = str(config["viewDistance"])
if not config.get("pvpEnabled", True):
environment["PVP"] = "false"
if config.get("allowFlight"):
environment["ALLOW_FLIGHT"] = "true"
if config.get("enableRCON"):
environment["ENABLE_RCON"] = "true"
if config.get("rconPassword"):
environment["RCON_PASSWORD"] = config["rconPassword"]
# Port bindings (host_port:container_port)
port = config.get("port", "25565")
port_bindings = {f"{25565}/tcp": int(port)}
if config.get("enableRCON"):
rcon_port = config.get("rconPort", "25575")
port_bindings[f"{25575}/tcp"] = int(rcon_port)
# Create or get volume
volume_name = f"{name}_data"
try:
client.volumes.get(volume_name)
except:
client.volumes.create(name=volume_name, labels={LABEL_MANAGED: LABEL_VALUE})
# Create container with proper volume mount
restart_policy = config.get("restartPolicy", "unless-stopped")
container = client.containers.create(
image="itzg/minecraft-server",
name=name,
environment=environment,
ports=port_bindings,
volumes=[f"{volume_name}:/data"],
labels={LABEL_MANAGED: LABEL_VALUE},
restart_policy={"Name": restart_policy} if restart_policy != "no" else None,
detach=True
)
return container
def start_container(client, container_id):
"""Start a container"""
container = client.containers.get(container_id)
container.start()
return container
def stop_container(client, container_id):
"""Stop a container"""
container = client.containers.get(container_id)
container.stop()
return container
def remove_container(client, container_id):
"""Remove a container"""
container = client.containers.get(container_id)
container.remove(force=True)
return container

195
backend/main.py Normal file
View file

@ -0,0 +1,195 @@
from fastapi import FastAPI, HTTPException, Depends
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from sqlalchemy.orm import Session
from pydantic import BaseModel
from typing import List, Optional
import json
from backend.database import get_db, Container, Base, engine
from backend.docker_manager import (
get_docker_client, list_containers, create_container,
start_container, stop_container, remove_container
)
# Create tables
Base.metadata.create_all(bind=engine)
app = FastAPI(title="Stronghold API")
# CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Mount static files (frontend will be served by nginx in production)
# app.mount("/", StaticFiles(directory="../frontend", html=True), name="static")
# Pydantic models
class ContainerConfig(BaseModel):
serverName: str
serverType: str
version: Optional[str] = "LATEST"
memory: Optional[str] = "2G"
port: Optional[str] = "25565"
difficulty: Optional[str] = None
gamemode: Optional[str] = None
levelType: Optional[str] = None
motd: Optional[str] = None
maxPlayers: Optional[int] = 20
viewDistance: Optional[int] = 10
acceptEULA: bool = True
enableRCON: bool = False
rconPort: Optional[str] = "25575"
rconPassword: Optional[str] = None
pvpEnabled: bool = True
allowFlight: bool = False
restartPolicy: str = "unless-stopped"
class ContainerResponse(BaseModel):
id: int
container_id: str
name: str
image: str
status: str
created_at: str
# API Routes
@app.get("/api/containers")
def get_containers(db: Session = Depends(get_db)):
"""List all Stronghold-managed containers"""
try:
client = get_docker_client()
docker_containers = list_containers(client)
# Get containers from database
db_containers = db.query(Container).all()
# Merge data
result = []
for db_container in db_containers:
# Find matching docker container
docker_container = next(
(dc for dc in docker_containers if dc.id == db_container.container_id),
None
)
status = docker_container.status if docker_container else "unknown"
result.append({
"id": db_container.id,
"container_id": db_container.container_id,
"name": db_container.name,
"image": db_container.image,
"status": status,
"created_at": db_container.created_at.isoformat() if db_container.created_at else None
})
return result
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/containers")
def create_container_endpoint(config: ContainerConfig, db: Session = Depends(get_db)):
"""Create a new Minecraft server container"""
try:
client = get_docker_client()
# Check if name already exists
existing = db.query(Container).filter(Container.name == config.serverName).first()
if existing:
raise HTTPException(status_code=400, detail="Container name already exists")
# Create container
docker_container = create_container(client, config.serverName, config.dict())
# Save to database
db_container = Container(
container_id=docker_container.id,
name=config.serverName,
image="itzg/minecraft-server",
status="created",
config_json=json.dumps(config.dict())
)
db.add(db_container)
db.commit()
db.refresh(db_container)
# Start container
docker_container.start()
# Update status
db_container.status = "running"
db.commit()
return {
"id": db_container.id,
"container_id": docker_container.id,
"name": config.serverName,
"status": "running"
}
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/containers/{container_id}/start")
def start_container_endpoint(container_id: str, db: Session = Depends(get_db)):
"""Start a container"""
try:
client = get_docker_client()
container = start_container(client, container_id)
# Update database
db_container = db.query(Container).filter(Container.container_id == container_id).first()
if db_container:
db_container.status = "running"
db.commit()
return {"status": "started", "container_id": container_id}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/containers/{container_id}/stop")
def stop_container_endpoint(container_id: str, db: Session = Depends(get_db)):
"""Stop a container"""
try:
client = get_docker_client()
container = stop_container(client, container_id)
# Update database
db_container = db.query(Container).filter(Container.container_id == container_id).first()
if db_container:
db_container.status = "stopped"
db.commit()
return {"status": "stopped", "container_id": container_id}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.delete("/api/containers/{container_id}")
def delete_container_endpoint(container_id: str, db: Session = Depends(get_db)):
"""Delete a container"""
try:
client = get_docker_client()
remove_container(client, container_id)
# Remove from database
db_container = db.query(Container).filter(Container.container_id == container_id).first()
if db_container:
db.delete(db_container)
db.commit()
return {"status": "deleted", "container_id": container_id}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get("/api/health")
def health_check():
"""Health check endpoint"""
return {"status": "ok"}

5
backend/requirements.txt Normal file
View file

@ -0,0 +1,5 @@
fastapi==0.104.1
uvicorn[standard]==0.24.0
docker==7.0.0
sqlalchemy==2.0.23

38
design.md Normal file
View file

@ -0,0 +1,38 @@
# Design
Take a lot of inspiration from crafty.
Make creating a modded server as simple as it is using ATLauncher
# Philosophy
KISS = Keep It Simple Stupid
dont make things over complicated
Keep code clean, readable, and concise.
Modular
# Plans
This web ui app should be ran in a docker container.
User authentication, permissions
simple status page for players to see if the server is up
# References
[itzg docker-minecraft-server](https://github.com/itzg/docker-minecraft-server)
[itzg docs](https://docker-minecraft-server.readthedocs.io/en/latest/) these docs exist in the main itzg github repo
[itzg minecraft proxy](https://github.com/itzg/docker-mc-proxy) used to setup proxy servers like velocity and bungee cord
[itzeg mc-router](https://github.com/itzg/mc-router) used to direct client to the right minecraft proxy or server based on requested server address
[crafty](https://craftycontrol.com)
[docker-rcon-web-admin](https://github.com/itzg/docker-rcon-web-admin)
[setupmc.com](https://setupmc.com/java-server/) Not open source but it's a web ui to generate yml files for docker using the itzg container image
# Maybe useful references
These are the github repos to other minecraft project that may be useful
[pterodactyl](https://github.com/pterodactyl/panel)
[fork](https://github.com/ForkGG/Fork) This is a Windows based server manager that may have useful ideas.
[MCSManager](https://github.com/MCSManager/MCSManager)
Minecraft launchers for clients. they have tools to download packs and server packs
[ATLauncher](https://github.com/ATLauncher/ATLauncher)
[Prism Launcher](https://github.com/PrismLauncher/PrismLauncher)

0
docker-compose Normal file
View file

24
docker-compose.yml Normal file
View file

@ -0,0 +1,24 @@
version: '3.8'
services:
stronghold:
build: .
ports:
- "8080:80"
volumes:
# Socket access for Podman (rootless)
# For Podman rootless, mount the socket to /var/run/docker.sock inside container
# This way the Docker SDK can find it at the standard location
- ${XDG_RUNTIME_DIR}/podman/podman.sock:/var/run/docker.sock:ro
# Database persistence
- ./data:/app/data
# Hot reload frontend (optional, for development)
- ./index.html:/usr/share/nginx/html/index.html:ro,z
- ./manage.html:/usr/share/nginx/html/manage.html:ro,z
environment:
# Set working directory for database
- PWD=/app/data
# Pass XDG_RUNTIME_DIR for Podman rootless
- XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-/run/user/1000}
working_dir: /app/data
restart: unless-stopped

852
index.html Normal file
View file

@ -0,0 +1,852 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Stronghold - Minecraft Server Generator</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 900px;
margin: 0 auto;
background: white;
border-radius: 12px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
padding: 40px;
}
h1 {
color: #333;
margin-bottom: 10px;
font-size: 2.5em;
}
.subtitle {
color: #666;
margin-bottom: 30px;
font-size: 1.1em;
}
.tabs {
display: flex;
border-bottom: 2px solid #e0e0e0;
margin-bottom: 30px;
}
.tab {
padding: 12px 24px;
background: none;
border: none;
cursor: pointer;
font-size: 1em;
font-weight: 500;
color: #666;
border-bottom: 3px solid transparent;
transition: all 0.3s;
}
.tab:hover {
color: #667eea;
}
.tab.active {
color: #667eea;
border-bottom-color: #667eea;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.form-section {
margin-bottom: 25px;
}
label {
display: block;
margin-bottom: 8px;
color: #333;
font-weight: 500;
font-size: 0.95em;
}
input, select, textarea {
width: 100%;
padding: 12px;
border: 2px solid #e0e0e0;
border-radius: 6px;
font-size: 1em;
transition: border-color 0.3s;
font-family: inherit;
}
input:focus, select:focus, textarea:focus {
outline: none;
border-color: #667eea;
}
textarea {
resize: vertical;
min-height: 80px;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
}
.form-row-3 {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 15px;
}
input[type="checkbox"] {
width: auto;
margin-right: 8px;
}
.checkbox-label {
display: flex;
align-items: center;
cursor: pointer;
}
.generate-btn {
width: 100%;
padding: 15px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 6px;
font-size: 1.1em;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
margin-top: 20px;
}
.generate-btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(102, 126, 234, 0.4);
}
.generate-btn:active {
transform: translateY(0);
}
.preview {
margin-top: 30px;
padding: 20px;
background: #f8f9fa;
border-radius: 6px;
border: 2px solid #e0e0e0;
}
.preview h3 {
margin-bottom: 15px;
color: #333;
}
pre {
background: #2d2d2d;
color: #f8f8f2;
padding: 15px;
border-radius: 6px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.85em;
line-height: 1.5;
max-height: 500px;
overflow-y: auto;
}
.download-btn {
width: 100%;
padding: 12px;
background: #28a745;
color: white;
border: none;
border-radius: 6px;
font-size: 1em;
font-weight: 600;
cursor: pointer;
margin-top: 15px;
transition: background 0.3s;
}
.download-btn:hover {
background: #218838;
}
.download-btn:disabled {
background: #ccc;
cursor: not-allowed;
}
.info-text {
font-size: 0.85em;
color: #666;
margin-top: 5px;
}
.section-title {
font-size: 1.2em;
font-weight: 600;
color: #333;
margin-bottom: 15px;
margin-top: 20px;
padding-bottom: 8px;
border-bottom: 2px solid #e0e0e0;
}
.modpack-type-section {
margin-bottom: 15px;
}
.modpack-input {
display: none;
}
.modpack-input.active {
display: block;
}
</style>
</head>
<body>
<div class="container">
<h1>🛡️ Stronghold</h1>
<p class="subtitle">Minecraft Server Docker Compose Generator</p>
<div class="tabs">
<button class="tab active" data-tab="basic">Basic Configuration</button>
<button class="tab" data-tab="modpack">Modpack</button>
<button class="tab" data-tab="advanced">Advanced</button>
</div>
<form id="serverForm">
<!-- Basic Configuration Tab -->
<div class="tab-content active" id="basic-tab">
<div class="form-section">
<label for="serverName">Server Name</label>
<input type="text" id="serverName" placeholder="my-minecraft-server" value="minecraft">
</div>
<div class="form-row">
<div class="form-section">
<label for="serverType">Server Type</label>
<select id="serverType">
<option value="VANILLA">Vanilla</option>
<option value="PAPER" selected>Paper (Recommended)</option>
<option value="FORGE">Forge (Modded)</option>
<option value="FABRIC">Fabric (Modded)</option>
<option value="SPIGOT">Spigot</option>
<option value="BUKKIT">Bukkit</option>
<option value="MAGMA">Magma (Mods + Plugins)</option>
</select>
</div>
<div class="form-section">
<label for="version">Minecraft Version</label>
<input type="text" id="version" placeholder="LATEST or 1.20.1" value="LATEST">
<span class="info-text">Use LATEST for newest version, or specify version like 1.20.1</span>
</div>
</div>
<div class="form-row">
<div class="form-section">
<label for="memory">Memory Allocation</label>
<select id="memory">
<option value="1G">1GB (Small)</option>
<option value="2G" selected>2GB (Recommended for most)</option>
<option value="4G">4GB (Medium)</option>
<option value="8G">8GB (Large/Modded)</option>
<option value="16G">16GB (Very Large)</option>
<option value="custom">Custom</option>
</select>
</div>
<div class="form-section" id="customMemorySection" style="display: none;">
<label for="customMemory">Custom Memory</label>
<input type="text" id="customMemory" placeholder="32G">
</div>
</div>
<div class="form-row">
<div class="form-section">
<label for="port">Server Port</label>
<input type="number" id="port" value="25565" min="1024" max="65535">
<span class="info-text">Default Minecraft port is 25565</span>
</div>
<div class="form-section">
<label for="difficulty">Difficulty</label>
<select id="difficulty">
<option value="">Default</option>
<option value="peaceful">Peaceful</option>
<option value="easy" selected>Easy</option>
<option value="normal">Normal</option>
<option value="hard">Hard</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-section">
<label for="gamemode">Default Game Mode</label>
<select id="gamemode">
<option value="">Default</option>
<option value="survival" selected>Survival</option>
<option value="creative">Creative</option>
<option value="adventure">Adventure</option>
<option value="spectator">Spectator</option>
</select>
</div>
<div class="form-section">
<label for="levelType">World Type</label>
<select id="levelType">
<option value="">Default</option>
<option value="DEFAULT">Default</option>
<option value="FLAT">Flat</option>
<option value="LARGEBIOMES">Large Biomes</option>
<option value="AMPLIFIED">Amplified</option>
<option value="CUSTOMIZED">Customized</option>
</select>
</div>
</div>
<div class="form-section">
<label for="motd">Server MOTD (Message of the Day)</label>
<input type="text" id="motd" placeholder="A Minecraft Server" maxlength="59">
<span class="info-text">Message shown in server list (max 59 characters)</span>
</div>
<div class="form-row">
<div class="form-section">
<label for="maxPlayers">Max Players</label>
<input type="number" id="maxPlayers" value="20" min="1" max="999">
</div>
<div class="form-section">
<label for="viewDistance">View Distance (chunks)</label>
<input type="number" id="viewDistance" value="10" min="3" max="32">
<span class="info-text">Higher = better visibility but more CPU usage</span>
</div>
</div>
<div class="form-section">
<label class="checkbox-label">
<input type="checkbox" id="acceptEULA" checked>
Accept Minecraft EULA
</label>
<span class="info-text">Required to run a Minecraft server</span>
</div>
</div>
<!-- Modpack Tab -->
<div class="tab-content" id="modpack-tab">
<div class="section-title">Modpack Installation</div>
<div class="form-section">
<label for="modpackType">Modpack Source</label>
<select id="modpackType">
<option value="none">No Modpack (Vanilla/Plugins only)</option>
<option value="curseforge">CurseForge Modpack</option>
<option value="modrinth">Modrinth Modpack</option>
<option value="url">Download from URL</option>
<option value="local">Local File Path</option>
</select>
</div>
<div class="modpack-input" id="curseforge-input">
<div class="form-section">
<label for="curseforgeFile">CurseForge Server Pack Path</label>
<input type="text" id="curseforgeFile" placeholder="/modpacks/modpack-server.zip">
<span class="info-text">Path to CurseForge server pack zip file inside container</span>
</div>
<div class="form-section">
<label for="useCurseforgeType">Use CurseForge TYPE</label>
<label class="checkbox-label">
<input type="checkbox" id="useCurseforgeType" checked>
Automatically set TYPE to CURSEFORGE
</label>
<span class="info-text">Uncheck if you want to manually set server type</span>
</div>
</div>
<div class="modpack-input" id="modrinth-input">
<div class="form-section">
<label for="modrinthProject">Modrinth Project ID or Slug</label>
<input type="text" id="modrinthProject" placeholder="allthemods">
<span class="info-text">Modrinth project identifier</span>
</div>
<div class="form-section">
<label for="modrinthVersion">Version ID (optional)</label>
<input type="text" id="modrinthVersion" placeholder="Leave empty for latest">
<span class="info-text">Specific version ID, or leave empty for latest</span>
</div>
</div>
<div class="modpack-input" id="url-input">
<div class="form-section">
<label for="modpackUrl">Modpack URL</label>
<input type="url" id="modpackUrl" placeholder="https://example.com/modpack.zip">
<span class="info-text">Direct download URL for modpack zip file</span>
</div>
</div>
<div class="modpack-input" id="local-input">
<div class="form-section">
<label for="modpackPath">Local Modpack Path</label>
<input type="text" id="modpackPath" placeholder="/modpacks/my-modpack.zip">
<span class="info-text">Path to modpack file inside container (use volume mount)</span>
</div>
</div>
<div class="form-section">
<label class="checkbox-label">
<input type="checkbox" id="removeOldMods">
Remove Old Mods/Plugins Before Installing
</label>
<span class="info-text">Deletes existing mods/plugins before installing modpack</span>
</div>
</div>
<!-- Advanced Tab -->
<div class="tab-content" id="advanced-tab">
<div class="section-title">RCON Configuration</div>
<div class="form-section">
<label class="checkbox-label">
<input type="checkbox" id="enableRCON">
Enable RCON (Remote Console)
</label>
</div>
<div id="rconConfig" style="display: none;">
<div class="form-row">
<div class="form-section">
<label for="rconPort">RCON Port</label>
<input type="number" id="rconPort" value="25575" min="1024" max="65535">
</div>
<div class="form-section">
<label for="rconPassword">RCON Password</label>
<input type="password" id="rconPassword" placeholder="changeit">
<span class="info-text">Choose a strong password</span>
</div>
</div>
</div>
<div class="section-title">Server Properties</div>
<div class="form-row">
<div class="form-section">
<label class="checkbox-label">
<input type="checkbox" id="pvpEnabled" checked>
Enable PVP
</label>
</div>
<div class="form-section">
<label class="checkbox-label">
<input type="checkbox" id="allowFlight">
Allow Flight
</label>
</div>
</div>
<div class="form-section">
<label for="maxTickTime">Max Tick Time (ms)</label>
<input type="number" id="maxTickTime" placeholder="-1 (no limit)" value="-1">
<span class="info-text">Maximum time a single tick can take (-1 = no limit)</span>
</div>
<div class="form-section">
<label for="ops">Server Operators (comma-separated Minecraft usernames)</label>
<input type="text" id="ops" placeholder="Player1,Player2">
<span class="info-text">Players who will have operator permissions</span>
</div>
<div class="section-title">Container Options</div>
<div class="form-row">
<div class="form-section">
<label for="restartPolicy">Restart Policy</label>
<select id="restartPolicy">
<option value="no">No</option>
<option value="always">Always</option>
<option value="on-failure">On Failure</option>
<option value="unless-stopped" selected>Unless Stopped</option>
</select>
</div>
<div class="form-section">
<label for="timezone">Timezone</label>
<input type="text" id="timezone" placeholder="America/New_York" value="UTC">
<span class="info-text">Server timezone (e.g., America/New_York, Europe/London)</span>
</div>
</div>
<div class="form-section">
<label class="checkbox-label">
<input type="checkbox" id="rollingLogs">
Enable Rolling Logs
</label>
<span class="info-text">Automatically delete old log files</span>
</div>
<div class="form-section">
<label class="checkbox-label">
<input type="checkbox" id="overrideServerProperties">
Override Server Properties
</label>
<span class="info-text">Allow environment variables to override server.properties</span>
</div>
</div>
<button type="submit" class="generate-btn" id="submitBtn">Create Server</button>
<button type="button" class="generate-btn" id="generateYamlBtn" style="margin-top: 10px; background: #6c757d;">Generate YAML Only</button>
</form>
<div id="preview" class="preview" style="display: none;">
<h3>Generated docker-compose.yml</h3>
<pre id="yamlPreview"></pre>
<button class="download-btn" id="downloadBtn">Download docker-compose.yml</button>
</div>
</div>
<script>
// Tab switching
document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', () => {
const targetTab = tab.dataset.tab;
// Update tabs
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
tab.classList.add('active');
// Update tab content
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
document.getElementById(`${targetTab}-tab`).classList.add('active');
});
});
// Modpack type switching
const modpackType = document.getElementById('modpackType');
modpackType.addEventListener('change', () => {
document.querySelectorAll('.modpack-input').forEach(input => {
input.classList.remove('active');
});
if (modpackType.value !== 'none') {
const activeInput = document.getElementById(`${modpackType.value}-input`);
if (activeInput) {
activeInput.classList.add('active');
}
}
});
// RCON config toggle
document.getElementById('enableRCON').addEventListener('change', (e) => {
document.getElementById('rconConfig').style.display = e.target.checked ? 'block' : 'none';
});
// Custom memory toggle
const memorySelect = document.getElementById('memory');
const customMemorySection = document.getElementById('customMemorySection');
memorySelect.addEventListener('change', () => {
customMemorySection.style.display = memorySelect.value === 'custom' ? 'block' : 'none';
});
// Form submission
const form = document.getElementById('serverForm');
const preview = document.getElementById('preview');
const yamlPreview = document.getElementById('yamlPreview');
const downloadBtn = document.getElementById('downloadBtn');
const API_BASE = window.location.origin;
// Generate YAML only (existing functionality)
document.getElementById('generateYamlBtn').addEventListener('click', () => {
const config = gatherFormData();
const yaml = generateDockerCompose(config);
yamlPreview.textContent = yaml;
preview.style.display = 'block';
downloadBtn.disabled = false;
downloadBtn.onclick = () => {
const blob = new Blob([yaml], { type: 'text/yaml' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'docker-compose.yml';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
});
// Create server (new functionality)
form.addEventListener('submit', async (e) => {
e.preventDefault();
const submitBtn = document.getElementById('submitBtn');
submitBtn.disabled = true;
submitBtn.textContent = 'Creating Server...';
try {
const config = gatherFormData();
// Submit to API
const response = await fetch(`${API_BASE}/api/containers`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(config),
});
const result = await response.json();
if (response.ok) {
// Show success message and redirect
preview.style.display = 'block';
preview.innerHTML = `
<h3 style="color: #28a745;">✓ Server Created Successfully!</h3>
<p><strong>Name:</strong> ${result.name}</p>
<p><strong>Status:</strong> ${result.status}</p>
<br>
<a href="manage.html" class="btn btn-primary" style="text-decoration: none; display: inline-block; padding: 10px 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 6px;">Go to Manage Page</a>
`;
} else {
throw new Error(result.detail || 'Failed to create server');
}
} catch (error) {
preview.style.display = 'block';
preview.innerHTML = `
<h3 style="color: #dc3545;">Error Creating Server</h3>
<p>${error.message}</p>
`;
} finally {
submitBtn.disabled = false;
submitBtn.textContent = 'Create Server';
}
});
function gatherFormData() {
return {
serverName: document.getElementById('serverName').value || 'minecraft',
serverType: document.getElementById('serverType').value,
version: document.getElementById('version').value || 'LATEST',
memory: memorySelect.value === 'custom'
? document.getElementById('customMemory').value || '2G'
: memorySelect.value,
port: document.getElementById('port').value || '25565',
difficulty: document.getElementById('difficulty').value,
gamemode: document.getElementById('gamemode').value,
levelType: document.getElementById('levelType').value,
motd: document.getElementById('motd').value,
maxPlayers: document.getElementById('maxPlayers').value,
viewDistance: document.getElementById('viewDistance').value,
acceptEULA: document.getElementById('acceptEULA').checked,
// Modpack
modpackType: document.getElementById('modpackType').value,
curseforgeFile: document.getElementById('curseforgeFile').value,
useCurseforgeType: document.getElementById('useCurseforgeType').checked,
modrinthProject: document.getElementById('modrinthProject').value,
modrinthVersion: document.getElementById('modrinthVersion').value,
modpackUrl: document.getElementById('modpackUrl').value,
modpackPath: document.getElementById('modpackPath').value,
removeOldMods: document.getElementById('removeOldMods').checked,
// RCON
enableRCON: document.getElementById('enableRCON').checked,
rconPort: document.getElementById('rconPort').value,
rconPassword: document.getElementById('rconPassword').value,
// Advanced
pvpEnabled: document.getElementById('pvpEnabled').checked,
allowFlight: document.getElementById('allowFlight').checked,
maxTickTime: document.getElementById('maxTickTime').value,
ops: document.getElementById('ops').value,
restartPolicy: document.getElementById('restartPolicy').value,
timezone: document.getElementById('timezone').value,
rollingLogs: document.getElementById('rollingLogs').checked,
overrideServerProperties: document.getElementById('overrideServerProperties').checked
};
}
function generateDockerCompose(config) {
let yaml = `services:
${config.serverName}:
image: itzg/minecraft-server
container_name: ${config.serverName}
ports:
- "${config.port}:25565"`;
// RCON port if enabled
if (config.enableRCON) {
yaml += `\n - "${config.rconPort}:25575"`;
}
yaml += `\n volumes:
- ${config.serverName}_data:/data`;
// Modpack volume mount if using local file
if (config.modpackType === 'curseforge' && config.curseforgeFile) {
yaml += `\n - ./modpacks:/modpacks:ro`;
} else if (config.modpackType === 'local' && config.modpackPath) {
const modpackDir = config.modpackPath.substring(0, config.modpackPath.lastIndexOf('/'));
if (modpackDir) {
yaml += `\n - ./modpacks:${modpackDir}:ro`;
}
}
yaml += `\n environment:`;
// EULA
if (config.acceptEULA) {
yaml += `\n EULA: "TRUE"`;
}
// Server Type
let serverType = config.serverType;
if (config.modpackType === 'curseforge' && config.useCurseforgeType) {
serverType = 'CURSEFORGE';
} else if (config.modpackType === 'modrinth') {
serverType = config.serverType; // Keep original or could be auto-detected
}
yaml += `\n TYPE: ${serverType}`;
// Version
if (config.version && config.version !== 'LATEST') {
yaml += `\n VERSION: ${config.version}`;
}
// Memory
if (config.memory) {
yaml += `\n MEMORY: ${config.memory}`;
}
// Modpack configuration
if (config.modpackType === 'curseforge' && config.curseforgeFile) {
yaml += `\n CF_SERVER_MOD: ${config.curseforgeFile}`;
} else if (config.modpackType === 'modrinth' && config.modrinthProject) {
yaml += `\n MODPACK: modrinth:${config.modrinthProject}`;
if (config.modrinthVersion) {
yaml += `:${config.modrinthVersion}`;
}
} else if (config.modpackType === 'url' && config.modpackUrl) {
yaml += `\n MODPACK: ${config.modpackUrl}`;
} else if (config.modpackType === 'local' && config.modpackPath) {
yaml += `\n MODPACK: ${config.modpackPath}`;
}
if (config.removeOldMods) {
yaml += `\n REMOVE_OLD_MODS: "TRUE"`;
}
// RCON
if (config.enableRCON) {
yaml += `\n ENABLE_RCON: "true"`;
if (config.rconPassword) {
yaml += `\n RCON_PASSWORD: ${config.rconPassword}`;
}
}
// Difficulty
if (config.difficulty) {
yaml += `\n DIFFICULTY: ${config.difficulty}`;
}
// Game Mode
if (config.gamemode) {
yaml += `\n MODE: ${config.gamemode}`;
}
// Level Type
if (config.levelType) {
yaml += `\n LEVEL_TYPE: ${config.levelType}`;
}
// MOTD
if (config.motd) {
yaml += `\n MOTD: "${config.motd}"`;
}
// Max Players
if (config.maxPlayers) {
yaml += `\n MAX_PLAYERS: ${config.maxPlayers}`;
}
// View Distance
if (config.viewDistance) {
yaml += `\n VIEW_DISTANCE: ${config.viewDistance}`;
}
// PVP
if (!config.pvpEnabled) {
yaml += `\n PVP: "false"`;
}
// Allow Flight
if (config.allowFlight) {
yaml += `\n ALLOW_FLIGHT: "true"`;
}
// Max Tick Time
if (config.maxTickTime && config.maxTickTime !== '-1') {
yaml += `\n MAX_TICK_TIME: ${config.maxTickTime}`;
}
// Ops
if (config.ops) {
yaml += `\n OPS: "${config.ops}"`;
}
// Timezone
if (config.timezone && config.timezone !== 'UTC') {
yaml += `\n TZ: ${config.timezone}`;
}
// Rolling Logs
if (config.rollingLogs) {
yaml += `\n ENABLE_ROLLING_LOGS: "true"`;
}
// Override Server Properties
if (config.overrideServerProperties) {
yaml += `\n OVERRIDE_SERVER_PROPERTIES: "true"`;
}
// Restart Policy
yaml += `\n restart: ${config.restartPolicy}`;
yaml += `\n\nvolumes:
${config.serverName}_data:`;
return yaml;
}
</script>
</body>
</html>

292
manage.html Normal file
View file

@ -0,0 +1,292 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Manage Servers - Stronghold</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 12px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
padding: 40px;
}
h1 {
color: #333;
margin-bottom: 10px;
font-size: 2.5em;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 1em;
font-weight: 500;
transition: all 0.3s;
text-decoration: none;
display: inline-block;
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.btn-success {
background: #28a745;
color: white;
}
.btn-danger {
background: #dc3545;
color: white;
}
.btn-warning {
background: #ffc107;
color: #333;
}
.btn-sm {
padding: 6px 12px;
font-size: 0.9em;
}
.servers-grid {
display: grid;
gap: 20px;
}
.server-card {
border: 2px solid #e0e0e0;
border-radius: 8px;
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.server-info h3 {
color: #333;
margin-bottom: 8px;
}
.server-info p {
color: #666;
margin: 4px 0;
font-size: 0.9em;
}
.status {
display: inline-block;
padding: 4px 12px;
border-radius: 12px;
font-size: 0.85em;
font-weight: 500;
}
.status-running {
background: #d4edda;
color: #155724;
}
.status-stopped {
background: #f8d7da;
color: #721c24;
}
.status-unknown {
background: #e2e3e5;
color: #383d41;
}
.server-actions {
display: flex;
gap: 10px;
}
.loading {
text-align: center;
padding: 40px;
color: #666;
}
.empty-state {
text-align: center;
padding: 60px 20px;
color: #666;
}
.empty-state h3 {
margin-bottom: 10px;
color: #333;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<div>
<h1>🛡️ Stronghold</h1>
<p style="color: #666;">Manage Your Minecraft Servers</p>
</div>
<a href="index.html" class="btn btn-primary">Create New Server</a>
</div>
<div id="serversContainer">
<div class="loading">Loading servers...</div>
</div>
</div>
<script>
const API_BASE = window.location.origin;
async function loadServers() {
try {
const response = await fetch(`${API_BASE}/api/containers`);
const servers = await response.json();
const container = document.getElementById('serversContainer');
if (servers.length === 0) {
container.innerHTML = `
<div class="empty-state">
<h3>No servers found</h3>
<p>Create your first Minecraft server to get started!</p>
<br>
<a href="index.html" class="btn btn-primary">Create Server</a>
</div>
`;
return;
}
container.innerHTML = '<div class="servers-grid"></div>';
const grid = container.querySelector('.servers-grid');
servers.forEach(server => {
const card = document.createElement('div');
card.className = 'server-card';
const statusClass = server.status === 'running' ? 'status-running' :
server.status === 'stopped' ? 'status-stopped' : 'status-unknown';
card.innerHTML = `
<div class="server-info">
<h3>${server.name}</h3>
<p><strong>Image:</strong> ${server.image}</p>
<p><strong>Status:</strong> <span class="status ${statusClass}">${server.status}</span></p>
<p><strong>Created:</strong> ${new Date(server.created_at).toLocaleString()}</p>
</div>
<div class="server-actions">
${server.status === 'running'
? `<button class="btn btn-warning btn-sm" onclick="stopServer('${server.container_id}', '${server.name}')">Stop</button>`
: `<button class="btn btn-success btn-sm" onclick="startServer('${server.container_id}', '${server.name}')">Start</button>`
}
<button class="btn btn-danger btn-sm" onclick="deleteServer('${server.container_id}', '${server.name}')">Delete</button>
</div>
`;
grid.appendChild(card);
});
} catch (error) {
document.getElementById('serversContainer').innerHTML =
`<div class="empty-state"><h3>Error loading servers</h3><p>${error.message}</p></div>`;
}
}
async function startServer(containerId, name) {
if (!confirm(`Start server "${name}"?`)) return;
try {
const response = await fetch(`${API_BASE}/api/containers/${containerId}/start`, {
method: 'POST'
});
const result = await response.json();
if (response.ok) {
alert('Server started successfully!');
loadServers();
} else {
alert('Error: ' + (result.detail || 'Unknown error'));
}
} catch (error) {
alert('Error: ' + error.message);
}
}
async function stopServer(containerId, name) {
if (!confirm(`Stop server "${name}"?`)) return;
try {
const response = await fetch(`${API_BASE}/api/containers/${containerId}/stop`, {
method: 'POST'
});
const result = await response.json();
if (response.ok) {
alert('Server stopped successfully!');
loadServers();
} else {
alert('Error: ' + (result.detail || 'Unknown error'));
}
} catch (error) {
alert('Error: ' + error.message);
}
}
async function deleteServer(containerId, name) {
if (!confirm(`Delete server "${name}"? This will remove the container and cannot be undone.`)) return;
try {
const response = await fetch(`${API_BASE}/api/containers/${containerId}`, {
method: 'DELETE'
});
const result = await response.json();
if (response.ok) {
alert('Server deleted successfully!');
loadServers();
} else {
alert('Error: ' + (result.detail || 'Unknown error'));
}
} catch (error) {
alert('Error: ' + error.message);
}
}
// Load servers on page load
loadServers();
// Refresh every 5 seconds
setInterval(loadServers, 5000);
</script>
</body>
</html>

103
references/SUMMARY.md Normal file
View file

@ -0,0 +1,103 @@
# Reference Projects Summary
This directory contains detailed analysis of reference projects for the Stronghold Minecraft server management tool.
## Files
1. **itzg-projects.md** - Core Docker projects by itzg that Stronghold will integrate with:
- docker-minecraft-server
- docker-mc-proxy
- mc-router
- docker-rcon-web-admin
2. **server-managers.md** - Existing web-based server management tools:
- Crafty (CraftyControl)
- Pterodactyl Panel
- Fork (ForkGG)
- MCSManager
- setupmc.com
3. **launchers.md** - Client-side launchers with excellent modpack workflows:
- ATLauncher
- Prism Launcher
4. **examples/** - Docker Compose example files from itzg repository:
- docker-compose-big.yml (Large Biomes world type)
- docker-compose-curseforge-atm7.yaml (All The Mods 7 modpack)
- docker-compose-curseforge.yml (Basic CurseForge example)
- docker-compose-magma.yml (Hybrid mods + plugins)
- docker-compose-rconcmd.yml (Advanced RCON commands)
- EXAMPLES-ANALYSIS.md (Detailed analysis of each example)
## Key Insights
### Architecture Recommendations
- **Keep it simple** (KISS): Docker API directly, no agents/daemons needed initially
- **Start without database**: Use file-based configuration, add database later if needed
- **Node.js stack**: Could work well (like MCSManager but simpler)
- **Single-host focus**: Start with single Docker host, expand later
### Feature Priorities
1. **Server CRUD**: Create, read, update, delete servers
2. **Configuration UI**: User-friendly interface for itzg container environment variables
3. **Console Access**: RCON integration for server management
4. **File Browser**: Access server files (worlds, configs, mods)
5. **Modpack Support**: ATLauncher-like experience for server creation
### Technical Stack Suggestions
- **Backend**: Node.js/Express or Python/FastAPI
- **Frontend**: React, Vue, or SvelteKit
- **Docker**: Direct Docker API integration
- **Storage**: File-based configs initially, optional database later
- **APIs**: Modrinth, CurseForge for modpack integration
### UI/UX Principles
- Take inspiration from Crafty and Fork for clean, intuitive interfaces
- Follow ATLauncher's model for modpack installation simplicity
- Use setupmc.com as reference for configuration UI patterns
- Prioritize common workflows over advanced features
### Implementation Phases
**MVP (Phase 1)**
- Basic Docker integration
- Simple server creation/management
- Basic web UI
- Console access
**V1.0 (Phase 2)**
- Full configuration UI
- File browser
- Modpack installation (basic)
- Proxy server support
**V1.5+ (Phase 3)**
- Modpack browser
- Advanced features
- Templates/presets
- Backup system
- mc-router integration
## Notes on Reference Projects
### Most Relevant
1. **itzg/docker-minecraft-server**: Core technology Stronghold will use
2. **itzg examples**: Real-world docker-compose configurations showing patterns and edge cases
3. **setupmc.com**: Shows how to simplify Docker config generation
4. **ATLauncher**: Target UX for modpack installation
5. **Crafty**: Good UI/UX patterns to follow
### Study but Don't Copy
1. **Pterodactyl**: Too complex, but has good separation of concerns
2. **MCSManager**: Good architecture ideas, but agent model overkill for Docker
3. **Fork**: Great UX, but Windows-focused
## Questions to Resolve
Based on these references, Stronghold needs to decide:
1. **Tech Stack**: Node.js vs Python vs Go?
2. **Database**: Start with files or include database from start?
3. **Multi-user**: Single-user MVP or multi-user from start?
4. **Deployment**: Single container, docker-compose, or microservices?
5. **Modpack APIs**: Which to support first (Modrinth, CurseForge, both)?

View file

@ -0,0 +1,207 @@
# Docker Compose Examples Analysis
This document analyzes various docker-compose.yml examples to identify patterns, edge cases, and implementation requirements for Stronghold.
## Common Patterns
### Basic Server Setup
**Pattern Elements**:
- Image: `itzg/minecraft-server`
- Required: `EULA=TRUE`
- Port mapping: `25565:25565`
- Volume: Persistent data storage
- Environment variables for configuration
### Modded Server Setup
**Pattern Elements**:
- `TYPE`: FORGE, FABRIC, or other mod loader
- `MODPACK` or `MODS`: Modpack/mod installation
- Additional memory allocation
- Mod volume mounts (`/mods`)
- Config volume mounts (`/config`)
### Plugin Server Setup
**Pattern Elements**:
- `TYPE`: PAPER, SPIGOT, BUKKIT
- Plugin volume mounts (`/plugins`)
- `PLUGINS` environment variable or volume
### Proxy Server Setup
**Pattern Elements**:
- Image: `itzg/mc-proxy`
- `PROXY_TYPE`: BUNGEE, VELOCITY, WATERFALL
- Backend server configuration
- Network linking
## Edge Cases to Handle
### 1. Multiple Mod Sources
```yaml
environment:
COPY_MODS_SRC: /mods-common,/mods-local
volumes:
- ../shared-mods:/mods-common:ro
- ./local-mods:/mods-local:ro
```
**Stronghold needs**: UI to manage multiple mod source directories
### 2. Generic Pack with Custom Configuration
```yaml
environment:
GENERIC_PACK: https://example.com/server-pack.zip
GENERIC_PACKS_DISABLE_MODS: "conflicting-mod.jar"
APPLY_EXTRA_FILES: "config/server.properties<https://example.com/custom.properties"
```
**Stronghold needs**: Support for generic packs and file patching
### 3. Environment Variable Substitution in Configs
```yaml
environment:
REPLACE_ENV_SUFFIXES: "yml,yaml,properties,json"
REPLACE_ENV_VARIABLES_EXCLUDES: "secrets.yml"
volumes:
- ./config:/config
```
**Stronghold needs**: UI for managing env var substitution rules
### 4. Auto-removal of Mods/Plugins
```yaml
environment:
REMOVE_OLD_MODS: "TRUE"
REMOVE_OLD_MODS_INCLUDE: "*.jar"
REMOVE_OLD_MODS_EXCLUDE: "core-mod.jar"
REMOVE_OLD_MODS_DEPTH: 1
```
**Stronghold needs**: Options for mod/plugin cleanup behavior
### 5. Multiple Generic Packs with Prefix/Suffix
```yaml
environment:
GENERIC_PACKS: configs-v9.0.1,mods-v4.3.6
GENERIC_PACKS_PREFIX: "https://cdn.example.org/"
GENERIC_PACKS_SUFFIX: ".zip"
```
**Stronghold needs**: Support for batch generic pack installation
### 6. Mod/Plugin URL Lists via File
```yaml
environment:
MODS_FILE: /extras/mods.txt
volumes:
- ./mods-list.txt:/extras/mods.txt:ro
```
**Stronghold needs**: File-based mod/plugin list management
### 7. Custom Copy Destinations
```yaml
environment:
COPY_PLUGINS_SRC: /plugins-custom
COPY_PLUGINS_DEST: /data/plugins
COPY_MODS_SRC: /mods-custom
COPY_MODS_DEST: /data/mods
COPY_CONFIG_SRC: /config-custom
COPY_CONFIG_DEST: /data/config
```
**Stronghold needs**: Advanced volume mount configuration options
### 8. Multi-Server Network with Proxy
**Complex Setup**:
- Multiple backend servers
- Proxy server
- Internal Docker network
- Port mapping coordination
- Service dependencies
**Stronghold needs**: Multi-container orchestration UI
### 9. Modpack Updates with Checksum Control
```yaml
environment:
MODPACK: modrinth:abc123
SKIP_GENERIC_PACK_CHECKSUM: "false"
SKIP_GENERIC_PACK_UPDATE_CHECK: "false"
FORCE_GENERIC_PACK_UPDATE: "true"
```
**Stronghold needs**: Modpack update management UI
### 10. Hybrid Server (Mods + Plugins)
```yaml
environment:
TYPE: MAGMA # or other hybrid type
USES_PLUGINS: "true"
volumes:
- ./mods:/mods
- ./plugins:/plugins
```
**Stronghold needs**: Detection and configuration of hybrid server types
## Configuration Complexity Levels
### Level 1: Simple
- Basic vanilla server
- Minimal configuration
- Default settings
**Stronghold UI**: Simple wizard, minimal options
### Level 2: Standard
- Common server types (Paper, Forge, Fabric)
- Standard modpack installation
- Basic resource allocation
**Stronghold UI**: Guided setup with sensible defaults
### Level 3: Advanced
- Custom configurations
- Multiple mod sources
- Environment variable substitution
- Custom volumes
**Stronghold UI**: Advanced options panel, expert mode
### Level 4: Expert
- Edge cases
- Manual docker-compose editing
- Complex network setups
- Custom scripts
**Stronghold UI**: YAML editor with validation, export/import
## Stronghold Implementation Priorities
### Phase 1 (MVP)
- Level 1 configurations (simple servers)
- Basic modpack installation (MODPACK env var)
- Standard volume mounts
### Phase 2 (V1.0)
- Level 2 configurations
- Individual mod/plugin management
- Multiple mod sources
- Basic generic pack support
### Phase 3 (V1.5+)
- Level 3 configurations
- Advanced volume mount options
- Environment variable substitution UI
- Multi-server orchestration
### Phase 4 (Future)
- Level 4 expert mode
- YAML editor/validator
- Import existing docker-compose files
- Template marketplace
## Key Takeaways
1. **Most users need Level 1-2**: Focus UI on these first
2. **Edge cases are powerful**: Support them but don't make them required
3. **Flexibility is key**: Allow export to docker-compose.yml for advanced users
4. **Validate configurations**: Check for conflicts and errors before applying
5. **Presets help**: Provide templates for common scenarios
6. **Documentation**: Link to itzg docs for advanced features

View file

@ -0,0 +1,370 @@
# Detailed Example Analysis
This document provides detailed analysis of each example docker-compose file from the itzg repository.
## 1. docker-compose-big.yml - Large Biomes World Type
**Source**: [itzg/docker-minecraft-server/examples/docker-compose-big.yml](https://github.com/itzg/docker-minecraft-server/blob/master/examples/docker-compose-big.yml)
### Purpose
Demonstrates configuration for a vanilla Minecraft server with:
- Large Biomes world generation
- High memory allocation (32G)
- RCON web admin integration
- Performance tuning settings
### Key Features
#### World Configuration
- `LEVEL_TYPE: LARGEBIOMES` - Generates worlds with larger biome sizes
#### Performance Settings
- `MAX_MEMORY: 32G` - Very high memory allocation (for large servers)
- `MAX_BUILD_HEIGHT: 256` - Standard build height
- `VIEW_DISTANCE: 15` - Moderate view distance
- `MAX_PLAYERS: 100` - High player capacity
- `CONSOLE: "false"` - Disables console (likely uses RCON instead)
#### Multi-Service Setup
- **minecraft**: Main server container
- **rcon**: Separate RCON web admin service (itzg/rcon)
- Exposes ports 4326 and 4327 for web admin access
- Uses separate volume for RCON database
### Stronghold Implementation Notes
1. **World Type Selection**: UI needs dropdown/selector for world types:
- DEFAULT
- FLAT
- LARGEBIOMES
- AMPLIFIED
- And modded world types (e.g., "biomesoplenty")
2. **Memory Presets**: Provide memory presets:
- Small (1-2G): Single player / small groups
- Medium (4-8G): Small servers
- Large (16G+): Large servers
- Custom: User-specified
3. **RCON Integration**: Show option to include RCON web admin:
- Separate service
- Automatic port configuration
- Link to web admin interface
4. **Performance Tuning**: Advanced options panel:
- View distance
- Max players
- Build height
- Console enable/disable
---
## 2. docker-compose-curseforge-atm7.yaml - All The Mods 7
**Source**: [itzg/docker-minecraft-server/examples/docker-compose-curseforge-atm7.yaml](https://github.com/itzg/docker-minecraft-server/blob/master/examples/docker-compose-curseforge-atm7.yaml)
### Purpose
Complete example for installing "All The Mods 7" modpack from CurseForge. This is a **comprehensive, well-documented example** that serves as an excellent template.
### Key Features
#### CurseForge Modpack Installation
- `TYPE: CURSEFORGE` - CurseForge modpack type
- `CF_SERVER_MOD: /modpacks/ATM7-0.4.32-server.zip` - Path to modpack server files
- Modpack zip file mounted from local `./modpacks` directory
#### Extensive Configuration
- **Memory**: `8G` - Appropriate for large modpacks
- **Timezone**: `America/New_York` - Server timezone configuration
- **Logging**: `ENABLE_ROLLING_LOGS: "true"` - Automatic log rotation
- **Game Options**: Extensive server properties override
#### Server Properties Override
- `OVERRIDE_SERVER_PROPERTIES: "true"` - Allows environment variable overrides
- Custom difficulty, view distance, max players
- PVP settings
- Level type (biomesoplenty for ATM7)
- MOTD customization
- Flight allowed
- Max tick time set to -1 (no timeout)
### Edge Cases Demonstrated
1. **EULA in Modpack**: Important note that EULA file in modpack.zip will overwrite EULA flag
2. **Local Modpack Files**: Shows mounting local modpacks directory
3. **Custom Container Name**: `container_name: mc_atm7`
4. **Restart Policy**: `unless-stopped` for persistence
### Stronghold Implementation Notes
1. **Modpack Installation Wizard**:
- Step 1: Select modpack source (CurseForge, Modrinth, local file, URL)
- Step 2: If local file, prompt for file path or upload
- Step 3: Auto-detect recommended memory based on modpack size/complexity
- Step 4: Configuration presets (can customize later)
2. **Configuration UI Sections**:
- **Basic**: EULA, memory, restart policy
- **Modpack**: Type, source, file path
- **Performance**: View distance, max players, tick time
- **Gameplay**: Difficulty, PVP, flight, MOTD
- **Advanced**: Override flags, timezone, logging
3. **Modpack Detection**:
- Parse modpack manifest to suggest:
- Server type (FORGE/FABRIC)
- Recommended memory
- Minecraft version
- Required mods/dependencies
4. **File Management**:
- UI for managing modpack files
- Upload/download modpack files
- Version selection
- Update notifications
5. **Template Generation**:
- Use this as a template for modpack installations
- Generate similar structure with user's selections
---
## 3. docker-compose-curseforge.yml - Basic CurseForge Example
**Source**: [itzg/docker-minecraft-server/examples/docker-compose-curseforge.yml](https://github.com/itzg/docker-minecraft-server/blob/master/examples/docker-compose-curseforge.yml)
### Purpose
Minimal example showing CurseForge modpack installation. Demonstrates simplicity for basic use cases.
### Key Features
#### Minimal Configuration
- Only essential variables
- `TYPE: CURSEFORGE`
- `CF_SERVER_MOD` pointing to modpack zip
- Uses environment variable substitution: `${IMAGE_TAG:-java8}`
#### Multiple Modpack Examples (Commented)
Shows flexibility with commented examples:
- Different modpack names
- URL-based modpack (direct download)
- Environment variable substitution for modpack selection
### Edge Cases Demonstrated
1. **Java Version Selection**: Uses `${IMAGE_TAG:-java8}` to select Java version
2. **URL Modpacks**: Shows that modpack can be a URL (direct download)
3. **Environment Variable Modpack Selection**: `${MODPACK:-default.zip}`
4. **Read-only Modpack Mount**: `./modpacks:/modpacks:ro`
### Stronghold Implementation Notes
1. **Simple Modpack Installation**:
- Minimal UI for quick setup
- Three input methods:
- Local file (browse/upload)
- URL (direct download)
- Modpack ID (if we integrate with CurseForge API)
2. **Java Version Selection**:
- Dropdown for Java version
- Auto-detect based on Minecraft version
- Allow manual override
3. **Quick Setup Mode**:
- Use this as "Simple" mode template
- Advanced options hidden by default
- "Show Advanced" toggle for power users
4. **Presets/Saved Configurations**:
- Save common modpack configurations
- Quick-switch between different modpacks
- Template library
---
## 4. docker-compose-magma.yml - Hybrid Mods + Plugins
**Source**: [itzg/docker-minecraft-server/examples/docker-compose-magma.yml](https://github.com/itzg/docker-minecraft-server/blob/master/examples/docker-compose-magma.yml)
### Purpose
Demonstrates Magma server type - a hybrid that supports both Forge mods AND Bukkit/Spigot plugins simultaneously.
### Key Features
#### Hybrid Server Type
- `TYPE: MAGMA` - Enables both mods and plugins
- `VERSION: 1.16.5` - Specific Minecraft version
- `image: itzg/minecraft-server:java8` - Java 8 required for this version
#### Interactive Container
- `tty: true` - Allocates a pseudo-TTY
- `stdin_open: true` - Keeps STDIN open
- Allows interactive console access
### Edge Cases Demonstrated
1. **Hybrid Server Type**: Unique server type requiring special handling
2. **Version-Specific Java**: Older versions require Java 8
3. **Interactive Mode**: Console access configuration
### Stronghold Implementation Notes
1. **Server Type Detection**:
- When user selects Magma (or other hybrid types):
- Show both "Mods" and "Plugins" tabs/sections
- Support both `/mods` and `/plugins` volume mounts
- Set `USES_PLUGINS=true` if needed
2. **Java Version Compatibility**:
- Map Minecraft versions to required Java versions
- Warn user if incompatible Java version selected
- Auto-select appropriate Java version
3. **Version-Specific Handling**:
- Some server types only work with specific Minecraft versions
- Validate compatibility
- Show warnings/errors
4. **Interactive Console**:
- Option to enable interactive console
- Useful for debugging
- May not be needed for most users
5. **Hybrid Server UI**:
- Special UI mode for hybrid servers
- Clear separation of mods vs plugins
- Different management for each type
---
## 5. docker-compose-rconcmd.yml - Advanced RCON Commands
**Source**: [itzg/docker-minecraft-server/examples/docker-compose-rconcmd.yml](https://github.com/itzg/docker-minecraft-server/blob/master/examples/docker-compose-rconcmd.yml)
### Purpose
Demonstrates advanced RCON usage with:
- Automated command execution at different lifecycle events
- CurseForge API integration for mod installation
- YAML heredoc syntax for multi-line commands
### Key Features
#### RCON Lifecycle Commands
- `RCON_CMDS_STARTUP` - Commands executed when server starts
- `RCON_CMDS_ON_CONNECT` - Commands executed when any player connects
- `RCON_CMDS_FIRST_CONNECT` - Commands executed on first player connection
- `RCON_CMDS_LAST_DISCONNECT` - Commands executed when last player disconnects
#### CurseForge API Integration
- `CURSEFORGE_FILES` - List of CurseForge mod names to auto-install
- `CF_API_KEY` - CurseForge API key from environment variable
- Requires API key from https://console.curseforge.com/
#### Advanced YAML Syntax
- Uses `|-` heredoc to remove leading/trailing newlines
- Multi-line command lists
- Proper formatting for readability
### Example Use Case
The example shows:
- Setting game rules on startup
- Creating teams for players
- Using Chunky (world pregeneration) mod
- Giving items to new players
- Managing entities when players disconnect
### Edge Cases Demonstrated
1. **Environment Variable Security**: API keys from `.env` file (not in compose file)
2. **YAML Heredoc Syntax**: Proper multi-line string handling
3. **CurseForge API Key**: Requires external API key management
4. **Lifecycle Hooks**: Automated server management via RCON
### Stronghold Implementation Notes
1. **RCON Command Management UI**:
- Tabs/sections for each lifecycle event
- Multi-line text editor for commands
- Command validation
- Preview of commands that will run
- Test/simulate commands
2. **CurseForge API Integration**:
- UI for managing API key (stored securely)
- Search/browse CurseForge mods by name
- Auto-install mods via API
- Dependency resolution
3. **Lifecycle Events**:
- **Startup**: Server initialization commands
- **On Connect**: Welcome messages, permissions, etc.
- **First Connect**: One-time setup (world generation, etc.)
- **Last Disconnect**: Cleanup, save, shutdown tasks
4. **Command Editor**:
- Syntax highlighting (Minecraft commands)
- Auto-completion
- Validation
- Help/cheat sheet
5. **Security**:
- Secure storage of API keys
- Environment variable management
- Never expose secrets in generated files
6. **Advanced Features**:
- Schedule commands (cron-like)
- Conditional commands
- Command templates
- Import/export command sets
---
## Common Patterns Across Examples
### 1. Volume Management
- All use named volumes or bind mounts for `/data`
- Some use read-only mounts for modpacks
- Consistent pattern: `volume_name:/data`
### 2. Environment Variables
- `EULA: "TRUE"` - Always required
- `TYPE` - Server type
- `MEMORY` - Memory allocation
- Port mapping: `25565:25565`
### 3. Restart Policies
- `always` - Production servers
- `unless-stopped` - Common for game servers
- `on-failure` - Development/testing
### 4. Image Tags
- Default: `itzg/minecraft-server`
- Java version: `:java8`, `:java17`, etc.
- Architecture: `:arm64`, `:amd64`, etc.
- Environment variable substitution common
## Implementation Priorities for Stronghold
### Phase 1 (MVP)
- Basic server creation (like curseforge.yml)
- Simple modpack installation
- Essential environment variables
### Phase 2 (V1.0)
- Advanced modpack configuration (like atm7.yaml)
- World type selection (like big.yml)
- RCON basic integration
### Phase 3 (V1.5)
- Hybrid server support (like magma.yml)
- RCON lifecycle commands (like rconcmd.yml)
- CurseForge API integration
### Phase 4 (Future)
- Multi-service orchestration
- Advanced RCON features
- Template library
- Import/export configurations

View file

@ -0,0 +1,48 @@
# Docker Compose Examples Reference
This directory contains example Docker Compose configurations for various Minecraft server setups using itzg containers.
## Purpose
These examples serve as:
- **Reference implementations** for common server configurations
- **Edge case demonstrations** for complex scenarios
- **Learning materials** for understanding itzg container capabilities
- **Templates** for Stronghold to generate similar configurations
## Categories
### Basic Examples
- Simple vanilla server
- Basic modded server (Forge/Fabric)
- Basic plugin server (Paper/Spigot)
### Advanced Examples
- Multi-server setups
- Proxy server configurations
- Modpack installations
- Custom configurations
### Edge Cases
- Complex volume mounts
- Multiple mod sources
- Environment variable substitution
- Generic pack installations
- Custom network configurations
## Source
Examples are typically sourced from:
- [itzg/docker-minecraft-server GitHub examples](https://github.com/itzg/docker-minecraft-server)
- Community contributions
- Documentation examples
- Real-world use cases
## Usage in Stronghold
Stronghold should:
1. Analyze these examples to understand configuration patterns
2. Generate similar configurations dynamically based on user input
3. Provide presets/templates based on common patterns
4. Handle edge cases gracefully in the UI

View file

@ -0,0 +1,28 @@
services:
minecraft:
ports:
- "25565:25565"
volumes:
- "mcbig:/data"
environment:
EULA: "TRUE"
MAX_MEMORY: 32G
MAX_BUILD_HEIGHT: 256
VIEW_DISTANCE: 15
LEVEL_TYPE: LARGEBIOMES
MAX_PLAYERS: 100
CONSOLE: "false"
image: itzg/minecraft-server
restart: always
rcon:
image: itzg/rcon
ports:
- "4326:4326"
- "4327:4327"
volumes:
- "rcon:/opt/rcon-web-admin/db"
volumes:
mcbig:
rcon:

View file

@ -0,0 +1,149 @@
####################################################################
# CURSEFORGE #
# #
# Date: 20221005 #
# #
# Mod: All The Mods 7 0.4.32 #
# #
# Notes: Verify that there is no EULA file in the modpack.zip #
# if you do not delete it the EULA flag below will be #
# overwritten when the modpack is copied. #
# #
####################################################################
services:
####################################################################
# Service Name #
# #
# Define Service Name here. If using RCON this name will be #
# referenced again as RWA_RCON_HOST below. #
# #
# Example: 'name:' or 'mc_atm6:' #
####################################################################
mc_atm7:
####################################################################
# Image & Container Name #
# #
# Specify Image Name and Java Version. The 'image' will always be #
# 'itzg/minecraft-server' however the tag added to the end is #
# where you can specify the java version or container architecture.#
# See readme.md for a full list. #
# #
# 'container_name:' This can be anything you like. This is the name#
# that will show when you run 'docker ps' commands. #
####################################################################
image: itzg/minecraft-server
container_name: mc_atm7
####################################################################
# Server Ports #
# #
# Specify external port. #
####################################################################
ports:
- 25565:25565
####################################################################
# Automatic Server Restart #
# #
# Define a restart policy here. #
# - 'no' = Do not restart. #
# - 'on-failure' = Restart if container exits because an error. #
# - 'always' = Regardless of stop reason. #
# - 'unless-stopped' = Similar to always except if stopped. #
####################################################################
restart: unless-stopped
####################################################################
# Volume and Folder Access #
# #
# This section defines what folders and volumes you want to give #
# this container access to. It is recommended to leaves these set #
# to the default values unless you know what you are doing. #
# #
# Place your mod zip file in a folder called 'modpacks' in the #
# same directory you place this docker-compose file. #
# #
# Specify the data volume name or directory here as well. #
# In this example the volume name is 'data'. When docker creates #
# the volume it will add what ever name you give it here to the #
# end of the container name specified above. In this example it #
# would be named 'mc_atm6_data'. If you change this be sure to #
# update the volume name at the bottom of this config. #
####################################################################
volumes:
- ./modpacks:/modpacks:ro
- data:/data
####################################################################
# EULA #
# #
# Accept EULA by setting to "true" #
####################################################################
environment:
EULA: "true"
####################################################################
# CURSEFORGE INSTALL #
# #
# Sets install type to FORGE and specifys the zip folder name #
# and location of your mod pack. #
# #
# TYPE: Defines the install type as CURSEFORGE #
# #
# CF_SERVER_MOD: Define where the modpack.zip is located. #
# #
# Place your mod zip file in a folder called 'modpacks' in the #
# same directory you place this docker-compose file. #
####################################################################
TYPE: CURSEFORGE
CF_SERVER_MOD: /modpacks/ATM7-0.4.32-server.zip
####################################################################
# Server Memory #
# #
# Set Maximum amount of memory allowed for your server. #
####################################################################
MEMORY: "8G"
####################################################################
# Logging Options #
# #
# Set to "true" to delete old logs #
################################################################ ####
ENABLE_ROLLING_LOGS: "true"
####################################################################
# Server Timezone #
# #
# Specify server Timezone #
####################################################################
TZ: "America/New_York"
####################################################################
# Minecraft Game Options #
# #
# List any game options you want to define here. A full list can #
# be found on the readme.md page on github. #
####################################################################
OVERRIDE_SERVER_PROPERTIES: "true"
DIFFICULTY: "easy"
MAX_TICK_TIME: "-1"
ALLOW_FLIGHT: "true"
OPS: ""
VIEW_DISTANCE: 10
MAX_PLAYERS: 10
PVP: "false"
LEVEL_TYPE: "biomesoplenty"
MOTD: "Welcome Home"
####################################################################
# Volumes #
# #
# Define data volume name here. You should leave this set to the #
# default. #
####################################################################
volumes:
data:

View file

@ -0,0 +1,20 @@
services:
mc:
image: itzg/minecraft-server:${IMAGE_TAG:-java8}
volumes:
- ./modpacks:/modpacks:ro
- data:/data
environment:
EULA: "true"
TYPE: CURSEFORGE
CF_SERVER_MOD: /modpacks/SIMPLE-SERVER-FILES-0.3.20.zip
# CF_SERVER_MOD: /modpacks/createlive3serverfiles+1.4.2.zip
# CF_SERVER_MOD: /modpacks/Valhelsia+3-3.5.1-SERVER.zip
# CF_SERVER_MOD: https://media.forgecdn.net/files/3012/800/SkyFactory-4_Server_4.2.2.zip
# CF_SERVER_MOD: /modpacks/${MODPACK:-SkyFactory_4_Server_4.1.0.zip}
ports:
- "25565:25565"
volumes:
data: {}

View file

@ -0,0 +1,16 @@
services:
mc:
image: itzg/minecraft-server:java8
tty: true
stdin_open: true
ports:
- "25565:25565"
environment:
EULA: "TRUE"
TYPE: MAGMA
VERSION: 1.16.5
volumes:
- data:/data
volumes:
data: {}

View file

@ -0,0 +1,40 @@
services:
minecraft:
image: ${IMAGE_TO_TEST:-itzg/minecraft-server}
ports:
- "25565:25565"
volumes:
- "mc:/data"
environment:
EULA: "TRUE"
TYPE: FABRIC
MEMORY: "2G"
CURSEFORGE_FILES: |
fabric-api
chunky-pregenerator
# Allocate API key from https://console.curseforge.com/
# and set in .env file making sure to double up dollar signs, such as
# CF_API_KEY=$$2a$$10$$....
# Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key
CF_API_KEY: ${CF_API_KEY}
# YAML Heredoc, be sure to use '|-' this will remove the first newline and final new line.
# This is versus '|' that will leaving with two empty strings at top and bottom.
RCON_CMDS_STARTUP: |-
/gamerule doFireTick false
/team add New
/team add Old
/chunky radius 1000
/chunky start
RCON_CMDS_ON_CONNECT: |-
/team join New @a[team=]
/give @a[team=New] birch_boat
/team join Old @a[team=New]
RCON_CMDS_FIRST_CONNECT: |-
/chunky pause
RCON_CMDS_LAST_DISCONNECT: |-
/kill @e[type=minecraft:boat]
/chunky continue
restart: unless-stopped
volumes:
mc: {}

283
references/itzg-projects.md Normal file
View file

@ -0,0 +1,283 @@
# itzg Projects Reference
These are the core Docker projects by itzg that Stronghold will integrate with.
## itzg/docker-minecraft-server
**GitHub**: https://github.com/itzg/docker-minecraft-server
**Docs**: https://docker-minecraft-server.readthedocs.io/en/latest/
### Overview
Docker image for running Minecraft Java Edition servers. Automatically downloads the specified version at startup.
### Key Features
- **Automatic Version Management**: Downloads and sets up the specified Minecraft version at startup
- **Server Type Support**: Supports multiple server types via `TYPE` environment variable:
- VANILLA
- PAPER (optimized, recommended)
- FORGE (modded servers)
- FABRIC (modded servers)
- SPIGOT
- BUKKIT
- And more
- **Configuration via Environment Variables**: Extensive configuration through environment variables for:
- Server properties (gamemode, difficulty, seed, etc.)
- Memory allocation (`MEMORY`)
- Java version selection
- Modpack installation
- Plugin management
- **Modpack Integration**:
- Supports Modrinth and CurseForge modpack installation
- Can download server packs automatically
- Handles mod installation and updates
- Feed the Beast (FTB) support
- Packwiz modpack support
- **Data Persistence**: Uses `/data` volume for:
- World data
- Server configurations
- Mods and plugins
- Logs
- **Health Monitoring**: Built-in health checks
- **RCON Support**: Built-in RCON support for remote console access
- **Resource Management**: Memory and CPU allocation via Docker
### Important Environment Variables
- `TYPE`: Server type (VANILLA, PAPER, FORGE, FABRIC, etc.)
- `VERSION`: Minecraft version (LATEST, specific version, or SNAPSHOT)
- `MEMORY`: JVM memory allocation
- `EULA`: Must be set to `TRUE` to accept Minecraft EULA
- `MOTD`: Server message of the day
- `DIFFICULTY`: Game difficulty
- `MODE`: Game mode (survival, creative, adventure, spectator)
- `MODPACK`: Modpack URL or ID for automatic installation
### Mods and Plugins Management
**Reference**: [itzg Mods and Plugins Documentation](https://docker-minecraft-server.readthedocs.io/en/latest/mods-and-plugins/)
#### Modpack Platforms Supported
- **Modrinth**: Auto-download modpacks from Modrinth
- **CurseForge**: Auto-download modpacks from CurseForge
- **Feed the Beast (FTB)**: FTB modpack support
- **Packwiz**: Packwiz format modpacks
#### Modpack Installation Methods
1. **MODPACK Environment Variable**:
- Set `MODPACK` to a zip file URL or container path
- Automatically downloads and installs modpack
- Works with zip files containing jar files at top level
2. **GENERIC_PACK / GENERIC_PACKS**:
- Install all server content (jars, mods, plugins, configs) from zip/tgz
- Can apply CurseForge modpacks missing server start script
- Supports multiple packs: `GENERIC_PACKS=pack1,pack2`
- Prefix/suffix support: `GENERIC_PACKS_PREFIX` and `GENERIC_PACKS_SUFFIX`
- Update checksum control: `SKIP_GENERIC_PACK_CHECKSUM`, `SKIP_GENERIC_PACK_UPDATE_CHECK`, `FORCE_GENERIC_PACK_UPDATE`
- Can disable specific mods: `GENERIC_PACKS_DISABLE_MODS`
#### Individual Mod/Plugin Installation
1. **MODS / PLUGINS Environment Variables**:
- Comma or newline delimited list of:
- URLs of jar files
- Container paths to jar files
- Container paths to directories containing jars
- Example:
```yaml
MODS: |
https://example.com/mod1.jar
/plugins/common/mod2.jar
```
- **Auto-removal**: Entries removed from list automatically remove files from server
- Empty list removes all mods/plugins
2. **MODS_FILE / PLUGINS_FILE**:
- Path or URL to text file listing mod/plugin URLs (one per line)
- Blank lines and lines starting with `#` are ignored
- Useful for managing large mod lists
#### Volume Mount Points for Mods/Plugins
1. **`/plugins` Volume**:
- Syncs into `/data/plugins` for plugin-based server types
- Customizable: `COPY_PLUGINS_SRC`, `COPY_PLUGINS_DEST`
- Set `USES_PLUGINS=true` for hybrid mod-based loaders using plugins
2. **`/mods` Volume**:
- Syncs into `/data/mods` for mod-based server types
- Customizable: `COPY_MODS_SRC`, `COPY_MODS_DEST`
3. **`/config` Volume**:
- Syncs into `/data/config` by default
- Customizable: `COPY_CONFIG_SRC`, `COPY_CONFIG_DEST`
- Environment variable processing on config files
- `SYNC_SKIP_NEWER_IN_DESTINATION=false` to force overwrite
4. **Multiple Source Directories**:
- `COPY_PLUGINS_SRC`, `COPY_MODS_SRC`, `COPY_CONFIG_SRC` can be comma/newline delimited lists
- Allows combining shared and project-specific mods/plugins
#### Environment Variable Processing
- Config files matching suffixes in `REPLACE_ENV_SUFFIXES` (default: "yml,yaml,txt,cfg,conf,properties,hjson,json,tml,toml")
- Exclusions: `REPLACE_ENV_VARIABLES_EXCLUDES`, `REPLACE_ENV_VARIABLES_EXCLUDE_PATHS`
- Disable with: `REPLACE_ENV_DURING_SYNC=false`
#### Removing Old Mods/Plugins
- **REMOVE_OLD_MODS=TRUE**: Delete old mods/plugins before installing new ones
- Fine-tuning options:
- `REMOVE_OLD_MODS_INCLUDE`: Comma-separated glob patterns to include
- `REMOVE_OLD_MODS_EXCLUDE`: Comma-separated glob patterns to exclude
- `REMOVE_OLD_MODS_DEPTH`: Maximum directory depth (default: 16)
- Default: Only jar files are removed
#### Additional Configuration Files
- **APPLY_EXTRA_FILES**: Download/copy additional config files before server starts
- Format: `destination<source_url` or `destination<source_path`
- Multiple entries with comma/newline separation
- Useful for baseline configs to be patched at runtime
- Processed before patch processing
#### Mods vs Plugins
- **Mods**: Client-side modifications for Forge/Fabric servers
- Modify rendering, add blocks, add behaviors
- Usually need to be installed on both client and server
- **Plugins**: Server-only modifications for Bukkit/Spigot/Paper
- Add behaviors, commands, features
- Only installed on server
- **Hybrid**: Some types like Magma use both mods and plugins
### Usage Pattern
Stronghold will need to:
1. Generate docker-compose.yml files or use Docker API to create containers
2. Set appropriate environment variables based on user configuration
3. Mount volumes for persistent data storage
4. Expose appropriate ports
5. Handle container lifecycle (start, stop, restart, delete)
---
## itzg/docker-mc-proxy
**GitHub**: https://github.com/itzg/docker-mc-proxy
### Overview
Docker image for running Minecraft proxy servers (BungeeCord, Waterfall, Velocity). Enables managing multiple Minecraft servers under a single proxy network.
### Key Features
- **Proxy Type Support**:
- BungeeCord
- Waterfall (BungeeCord fork)
- Velocity
- **Configuration Management**:
- Configuration files via volume mounts
- Environment variable substitution in config files
- Automatic config synchronization
- **Plugin Support**:
- Plugins mounted via volumes
- Supports proxy-specific plugins
- **Port Configuration**:
- Default proxy port (25577) can be mapped
- Custom port configuration
- **Health Checks**: Built-in health monitoring
- **Java Version Support**: Multiple Java version tags available
- **Multi-Architecture**: Supports multiple CPU architectures
### Usage Pattern
Stronghold will need to:
1. Create proxy containers with appropriate type
2. Configure proxy config files (config.yml, servers.yml, etc.)
3. Link backend servers to the proxy
4. Manage plugin installation
5. Handle port mapping and routing
---
## itzg/mc-router
**GitHub**: https://github.com/itzg/mc-router
### Overview
Lightweight TCP reverse proxy/multiplexer for Minecraft Java servers. Routes client connections to backend servers based on the requested server address (SRV records or subdomain).
### Key Features
- **Dynamic Routing**: Routes based on server address requested by client
- **Docker Auto-Discovery**: Automatically discovers running Minecraft containers
- **Health Checks**: Monitors backend server health
- **Load Balancing**: Can distribute connections across multiple servers
- **REST API**: Provides REST API for managing routes
- **High Availability**: Redirects to operational servers if one fails
- **Configuration**:
- Can be configured via environment variables
- Supports static route configuration
- Docker labels for dynamic discovery
### Usage Pattern
Stronghold will need to:
1. Deploy mc-router container alongside servers
2. Configure routing rules (static or dynamic)
3. Use Docker labels for auto-discovery
4. Manage backend server registrations via REST API
5. Monitor route health and availability
---
## itzg/docker-rcon-web-admin
**GitHub**: https://github.com/itzg/docker-rcon-web-admin
### Overview
Web-based admin interface for Minecraft servers via RCON (Remote Console).
### Key Features
- **RCON Integration**: Connects to Minecraft servers via RCON protocol
- **Web Interface**: Browser-based console access
- **Command Execution**: Execute server commands remotely
- **Log Viewing**: View server logs in real-time
- **Docker Integration**: Runs as a side-car container
### Usage Pattern
Stronghold may want to:
1. Integrate RCON functionality directly rather than using separate container
2. Use this as reference for RCON implementation
3. Potentially use as a fallback/admin interface
---
## Key Takeaways for Stronghold
1. **Environment Variable Configuration**: All itzg containers use extensive environment variable configuration. Stronghold needs a robust system for:
- UI to configure these variables
- Validating configurations
- Generating docker-compose.yml files or Docker API calls
2. **Volume Management**: Need to manage Docker volumes for:
- Server data persistence
- Configuration files
- Mods/plugins
- Backups
3. **Network Management**: Need to handle:
- Port allocation and mapping
- Internal Docker networking for proxy/server communication
- External port exposure
4. **Lifecycle Management**: Complete container lifecycle:
- Create (with proper configuration)
- Start/Stop/Restart
- Update/Upgrade
- Delete (with data handling)
5. **Integration Points**:
- Docker API for container management
- RCON for server console access
- Health checks for monitoring
- REST APIs (mc-router) for advanced routing
6. **Mod/Plugin Management Complexity**: The itzg container provides extensive mod/plugin management options:
- Multiple modpack platforms (Modrinth, CurseForge, FTB, Packwiz)
- Multiple installation methods (MODPACK, GENERIC_PACK, MODS/PLUGINS vars, file lists)
- Volume mounts for mods/plugins/configs
- Auto-removal of mods/plugins
- Environment variable substitution in configs
- Stronghold UI should simplify these options while exposing flexibility when needed
- Consider wizard-based flows for common scenarios (modpack installation) vs advanced configuration for power users

210
references/launchers.md Normal file
View file

@ -0,0 +1,210 @@
# Minecraft Launcher Reference
These are client-side Minecraft launchers that have excellent modpack installation workflows. Stronghold should aim to make server modpack installation as easy as these launchers make client installation.
## ATLauncher
**GitHub**: https://github.com/ATLauncher/ATLauncher
**Website**: https://atlauncher.com
### Overview
Minecraft launcher focused on modpack installation and management. Known for making modpack installation simple and user-friendly.
### Key Features
- **Modpack Browser**: Browse and search modpacks from various sources
- **One-Click Installation**: Install modpacks with minimal configuration
- **Instance Management**: Create multiple Minecraft instances with different modpacks
- **Modpack Updates**: Easy updating of installed modpacks
- **Server Pack Generation**: Can generate server packs from modpacks
- **Integration with Modpack Platforms**:
- CurseForge
- Modrinth
- FTB (Feed The Beast)
- **Version Management**: Manage Minecraft and mod versions
- **Mod Management**: View, enable, disable individual mods
- **Configuration**: Edit modpack configurations easily
### User Experience Highlights
- Simple, clean interface
- Clear categorization of modpacks
- Detailed modpack information before installation
- Progress indicators during installation
- Pre-configured settings that "just work"
- Easy troubleshooting
### Lessons for Stronghold
**This is a KEY reference** - the goal is to make modded server creation as simple as ATLauncher makes modpack installation.
Stronghold should:
1. **Modpack Browser**: Provide UI to browse/search modpacks
2. **One-Click Server Creation**: Create modded server from modpack in one click
3. **Server Pack Handling**: Automatically detect and install server packs
4. **Configuration Simplification**: Hide complexity, provide good defaults
5. **Mod Management**: Allow viewing/managing mods in installed servers
6. **Update Management**: Easy way to update modpacks
**Key Insight**: The magic of ATLauncher is that it handles all the complexity (downloading, dependency resolution, configuration, version matching) behind the scenes. Stronghold needs to do the same for servers.
---
## Prism Launcher
**GitHub**: https://github.com/PrismLauncher/PrismLauncher
**Website**: https://prismlauncher.org
### Overview
Fork of MultiMC, focused on modpack management. Open source and community-driven.
### Key Features
- **Multi-Platform**: Windows, macOS, Linux
- **Modpack Support**:
- Modrinth integration
- CurseForge integration
- FTB integration
- Technic Platform support
- **Instance Management**: Multiple instances with isolated configurations
- **Mod Management**: Individual mod installation, removal, updates
- **Version Control**: Git integration for instance management
- **Metadata Management**: Rich metadata for modpacks and mods
- **Customization**: Highly customizable interface and workflows
### Technology Stack
- C++/Qt for desktop application
- Various APIs for modpack platforms
### Lessons for Stronghold
- **Modrinth/CurseForge APIs**: Need to integrate with these platforms
- **Metadata Handling**: Rich metadata helps users make informed choices
- **Version Management**: Proper version handling is critical
- **Instance Isolation**: Each server should be isolated (Docker helps here)
### API Integration Notes
Both ATLauncher and Prism Launcher integrate with:
- **Modrinth API**: Modern modpack/mod platform
- **CurseForge API**: Legacy but still popular platform
- **FTB API**: Feed The Beast modpacks
Stronghold will need to:
1. Integrate with these APIs for modpack discovery
2. Handle server pack downloads from these platforms
3. Parse modpack metadata (version, dependencies, server pack info)
4. Manage modpack installation in Docker containers
---
## Key Takeaways for Stronghold
### The ATLauncher Experience, But for Servers
When a user wants to create a modded server, the flow should be:
1. **Browse Modpacks**: Search/browse available modpacks (like ATLauncher)
2. **Select Modpack**: Click on a modpack to see details
3. **Create Server**: One click to create server from modpack
4. **Automatic Setup**: Stronghold handles:
- Downloading server pack
- Setting up Docker container with correct server type (FORGE/FABRIC)
- Installing mods
- Configuring environment variables
- Setting up volumes
- Starting server
5. **Ready to Play**: Server is ready, user can customize further if needed
### Technical Requirements
1. **Modpack Platform Integration**:
- Modrinth API client
- CurseForge API client
- FTB API (if needed)
2. **Modpack Processing**:
- Parse modpack manifest files
- Download modpack files
- Extract server packs
- Handle mod dependencies
- Version resolution
3. **Docker Integration**:
- Detect server type (FORGE/FABRIC) from modpack
- Set appropriate `TYPE` environment variable
- Configure modpack installation
- Set up proper Java versions
- Allocate appropriate memory
4. **UI Components**:
- Modpack browser/search
- Modpack detail view
- Installation progress
- Mod management UI
- Server configuration tied to modpack
### Simplification Goals
**From User Perspective:**
- Before: User needs to know about modpack formats, server packs, FORGE vs FABRIC, Java versions, memory allocation, Docker, etc.
- After: User finds modpack, clicks "Create Server", server is ready
**Implementation Complexity:**
- Handle all the technical details internally
- Provide smart defaults
- Allow customization but don't require it
- Good error messages if something goes wrong
### Implementation Phases
**Phase 1: Basic Modpack Support**
- Manual modpack URL/ID input
- Support for Modrinth server packs
- Basic installation
**Phase 2: Modpack Browser**
- Integrate Modrinth API
- Browse/search modpacks
- Server pack detection and installation
**Phase 3: Enhanced Features**
- CurseForge integration
- Modpack updates
- Mod management UI
- Advanced configuration options
---
## Related Tools
### Server Pack Generators
Some modpacks come with server pack generators. Stronghold should:
- Detect if server pack is available
- Download server pack automatically
- Use itzg container's built-in modpack support when possible
- Handle cases where manual setup is needed
### itzg Container Modpack Support
The itzg/docker-minecraft-server container has extensive built-in modpack support ([Documentation](https://docker-minecraft-server.readthedocs.io/en/latest/mods-and-plugins/)):
**Modpack Platforms**:
- Modrinth (auto-download)
- CurseForge (auto-download)
- Feed the Beast (FTB)
- Packwiz format
**Installation Methods**:
- `MODPACK` environment variable: Simple zip file or modpack ID
- `GENERIC_PACK` / `GENERIC_PACKS`: Install complete server content from archives
- Individual mods via `MODS` / `PLUGINS` environment variables
- File-based lists via `MODS_FILE` / `PLUGINS_FILE`
**Advanced Features**:
- Volume mounts for mods/plugins/configs (`/mods`, `/plugins`, `/config`)
- Auto-removal of mods/plugins
- Environment variable substitution in configs
- Multiple source directories
- Disable specific mods
**Key Insight**: The container handles much of the complexity internally. Stronghold's role is to:
1. Provide UI for modpack selection/browsing
2. Detect modpack platform and server type automatically
3. Generate appropriate environment variables
4. Handle the container creation with proper configuration
5. Manage modpack updates and mod additions/removals
Stronghold should leverage this built-in support heavily to simplify implementation - we don't need to reimplement modpack processing, just orchestrate it via the container's environment variables.

View file

@ -0,0 +1,207 @@
# Server Management Web UIs Reference
These are existing web-based Minecraft server management tools that can provide inspiration for Stronghold's UI/UX and feature set.
## Crafty (CraftyControl)
**Website**: https://craftycontrol.com
### Overview
Web-based Minecraft server management tool. Stronghold takes inspiration from Crafty's approach to server management.
### Notable Features (based on common patterns)
- Web-based interface for managing servers
- Docker-based server hosting
- Server creation and configuration UI
- Console access
- File browser/editor
- Backup management
- Plugin/mod management
- User management (multi-user support)
- Server templates/presets
### Design Philosophy
- User-friendly interface
- Simplified server management
- Docker containerization
- Self-hosted solution
### Lessons for Stronghold
- Keep the UI clean and intuitive (KISS principle)
- Make common tasks easy to accomplish
- Provide good defaults with customization options
- Focus on Docker-based deployment
---
## Pterodactyl Panel
**GitHub**: https://github.com/pterodactyl/panel
### Overview
Open-source game server management panel. Supports multiple game types including Minecraft. Uses a client-server architecture with Wings (daemon) and Panel (web UI).
### Architecture
- **Panel**: Web UI (Laravel/PHP)
- **Wings**: Daemon running on game servers (Go)
- **API**: RESTful API between Panel and Wings
- **Database**: MySQL/PostgreSQL
### Key Features
- Multi-server management
- User management and permissions
- Resource allocation (CPU, memory, disk)
- File manager with code editor
- Console access
- Backup system
- Server templates
- Plugin/mod installation
- Resource monitoring
- SFTP access
- Multi-node support (Wings daemons on multiple hosts)
### Technology Stack
- Backend: Laravel (PHP)
- Daemon: Go
- Frontend: React/Vue components
- Database: MySQL/PostgreSQL
### Lessons for Stronghold
- **Pros**:
- Comprehensive feature set
- Multi-user with permissions
- Good separation of concerns (Panel/Daemon)
- Resource management
- **Cons/Considerations**:
- Complex architecture (may violate KISS)
- Requires database
- More overhead than needed for single-host Docker setup
- Can be over-engineered for simple use cases
**Takeaway**: Consider simplified version - Pterodactyl is powerful but complex. Stronghold can be simpler since it's focused on Docker containers rather than multi-node deployments.
---
## Fork (ForkGG)
**GitHub**: https://github.com/ForkGG/Fork
### Overview
Windows-based Minecraft server manager (not web-based). Built with .NET.
### Key Features
- Simple, intuitive UI
- Server creation wizard
- Console management
- File browser
- Plugin/mod installation
- Server templates
- Backup management
- Resource monitoring
### Design Philosophy
- Windows-focused
- User-friendly
- Simplified workflows
### Lessons for Stronghold
- Good UX patterns for server management
- Simple workflows for common tasks
- Focus on ease of use
- Wizard-based server creation (good for beginners)
**Note**: Being Windows-focused limits direct applicability, but UX patterns are valuable.
---
## MCSManager
**GitHub**: https://github.com/MCSManager/MCSManager
### Overview
Open-source Minecraft server management panel. Built with Node.js.
### Architecture
- **Web UI**: React-based frontend
- **Backend**: Node.js/Express API
- **Agent**: Daemon running on servers (also Node.js)
- **Database**: MongoDB (or can use file-based)
### Key Features
- Web-based interface
- Multi-server management
- Console access
- File manager
- Task scheduling
- Backup system
- User management
- Server templates
- Remote server management (via agents)
- REST API
### Technology Stack
- Backend: Node.js/Express
- Frontend: React
- Database: MongoDB (optional, can use files)
- Agents: Node.js daemons
### Lessons for Stronghold
- **Pros**:
- Node.js stack (good for web-based tools)
- Can work without database (file-based)
- REST API architecture
- Good separation (UI/API/Agent)
- **Considerations**:
- Agent-based architecture (may not be needed for Docker-only)
- Can be complex
- MongoDB dependency (unless using file mode)
**Takeaway**: Node.js stack could work well for Stronghold. The agent model might be overkill if we're just managing Docker containers directly.
---
## setupmc.com
**Website**: https://setupmc.com/java-server/
### Overview
Web UI for generating docker-compose.yml files for itzg docker-minecraft-server. Not open source but provides a good example of a configuration generator.
### Key Features
- Interactive web form for server configuration
- Generates docker-compose.yml files
- Handles various server types
- Configuration presets
- Downloadable configuration files
### Lessons for Stronghold
- **Inspiration**: This is essentially a subset of what Stronghold should do
- Shows how to make Docker configuration user-friendly
- Good example of configuration UI patterns
- Proves the value of simplifying Docker setup
**Takeaway**: Stronghold should go beyond just generating configs - should also manage the actual containers, but this is a good reference for the configuration UI.
---
## Comparison Summary
| Feature | Crafty | Pterodactyl | Fork | MCSManager | setupmc.com |
|---------|--------|-------------|------|------------|-------------|
| Web UI | ✅ | ✅ | ❌ | ✅ | ✅ |
| Docker-based | ✅ | ❌ (Wings) | ❌ | ❌ (Agents) | ✅ |
| Multi-user | ✅ | ✅ | ❌ | ✅ | ❌ |
| Complexity | Medium | High | Low | Medium | Low |
| Database | Yes | Yes | No | Optional | No |
| Architecture | Simple | Panel/Daemon | Desktop | UI/API/Agent | Static site |
### Recommendations for Stronghold
1. **KISS Principle**: Keep architecture simple - Docker API directly, no agents/daemons needed
2. **Database**: Start without database (file-based config), add later if needed
3. **Stack**: Node.js/Express + React could be good (like MCSManager but simpler)
4. **Features**: Focus on essential features first:
- Server CRUD operations
- Configuration UI
- Console access
- File browser
- Basic backups
5. **Multi-user**: Can add later, not essential for MVP
6. **UI Patterns**: Study Crafty and Fork for UX inspiration
7. **Configuration**: Use setupmc.com as reference for config UI patterns