Skip to content

Commit d7b3725

Browse files
committed
Process feedback
1 parent 092f463 commit d7b3725

5 files changed

Lines changed: 130 additions & 141 deletions

File tree

ix-dev/community/calagopus/app.yaml

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,14 @@
11
app_version: 1.0.3
2-
capabilities:
3-
- description: Calagopus is able to change file ownership arbitrarily
4-
name: CHOWN
5-
- description: Calagopus is able to bypass file permission checks
6-
name: DAC_OVERRIDE
7-
- description: Calagopus is able to bypass permission checks for file operations
8-
name: FOWNER
9-
- description: Calagopus is able to send signals to any process
10-
name: KILL
11-
- description: Calagopus is able to set file capabilities on other files
12-
name: SETFCAP
13-
- description: Calagopus is able to change group ID of processes
14-
name: SETGID
15-
- description: Calagopus is able to transfer capabilities between processes
16-
name: SETPCAP
17-
- description: Calagopus is able to change user ID of processes
18-
name: SETUID
192
categories:
203
- games
214
date_added: '2026-04-28'
22-
description: Calagopus is a modern, open-source game server management panel built
5+
description: A modern game server management panel built
236
in Rust. Deploy, monitor, and manage Minecraft, Hytale, and other game servers with
247
industry-leading performance.
258
home: https://calagopus.com/
269
host_mounts:
2710
- description: Docker socket
2811
host_path: /var/run/docker.sock
29-
- description: Wings configuration directory\
30-
host_path: /etc/pterodactyl
31-
- description: Wings data directory
32-
host_path: /var/lib/pterodactyl
33-
- description: Wings temporary directory
34-
host_path: /tmp/pterodactyl
3512
icon: https://media.sys.truenas.net/apps/calagopus/icons/icon.svg
3613
keywords:
3714
- calagopus

ix-dev/community/calagopus/ix_values.yaml

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,6 @@ consts:
2828
data_path: /var/lib/calagopus
2929
log_path: /var/log/calagopus
3030
wings_config_path: /var/lib/calagopus/wings-config.yml
31-
wings_etc_path: /etc/pterodactyl
32-
wings_data_path: /var/lib/pterodactyl
33-
wings_tmp_path: /tmp/pterodactyl
3431
notes_body: |
3532
The first time you visit the panel you will be greeted by the Out Of Box
3633
Experience (OOBE) wizard which lets you create the initial administrator
@@ -40,18 +37,16 @@ consts:
4037
registered for you on first start. You can begin creating servers from the
4138
panel right away.
4239
43-
Game server data is stored on the host under `/var/lib/pterodactyl/volumes`.
44-
This path cannot be changed because Wings spawns sibling containers via the
45-
Docker socket and those containers reference host paths that must match the
46-
paths inside the Calagopus container. If you need server data on a different
47-
pool, create a bind mount from your desired location to `/var/lib/pterodactyl`
48-
on the host before installing this app.
40+
Game server volumes, backups, archives and Wings's working directories are
41+
all stored under the dataset you select for "Wings Data Storage". Wings
42+
spawns sibling containers via the Docker socket; those sibling containers
43+
bind-mount paths under the Wings data dataset directly from the host, so
44+
that dataset is mounted into the Calagopus container at the same path it
45+
has on the host.
4946
50-
Wings logs are written inside the container at `/var/log/pterodactyl` and are
51-
not persisted to a host directory (TrueNAS does not allow apps to mount paths
52-
under `/var/log`). The Panel logs (`/var/log/calagopus`) are persisted via the
53-
"Calagopus Logs Storage" option, and Wings logs remain accessible through
54-
`docker logs` on the container.
47+
Wings runtime logs are written under the same Wings data dataset (under
48+
`<wings_data>/logs`). Panel logs are persisted separately via the
49+
"Calagopus Logs Storage" option.
5550
5651
For details on every available environment variable, see the upstream
5752
environment documentation: https://calagopus.com/docs/panel/environment

ix-dev/community/calagopus/questions.yaml

Lines changed: 87 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -56,23 +56,6 @@ questions:
5656
default: ""
5757
required: true
5858
private: true
59-
- variable: app_primary
60-
label: Primary Instance
61-
description: |
62-
Designates whether this Panel instance is the primary one in a clustered environment.</br>
63-
The primary instance is responsible for running background tasks (e.g. cleanup jobs).</br>
64-
Leave this enabled unless you are running multiple Panel instances against the same database.
65-
schema:
66-
type: boolean
67-
default: true
68-
- variable: app_debug
69-
label: Enable Debug Mode
70-
description: |
71-
When enabled, the Panel produces more verbose logs and detailed error pages.</br>
72-
Useful for troubleshooting but should be disabled in production as it can expose sensitive information.
73-
schema:
74-
type: boolean
75-
default: false
7659
- variable: enable_wings_proxy
7760
label: Enable Wings Proxy
7861
description: |
@@ -82,40 +65,6 @@ questions:
8265
schema:
8366
type: boolean
8467
default: true
85-
- variable: use_decryption_cache
86-
label: Use Decryption Cache
87-
description: |
88-
Caches decrypted values in memory and Redis for improved performance.</br>
89-
This is slightly less secure since decrypted data is held in the cache.
90-
schema:
91-
type: boolean
92-
default: true
93-
- variable: use_internal_cache
94-
label: Use Internal Cache
95-
description: |
96-
Caches short-lived values in app memory in addition to Redis.</br>
97-
Improves performance at the cost of slightly higher memory usage.
98-
schema:
99-
type: boolean
100-
default: true
101-
- variable: server_name
102-
label: Server Name
103-
description: |
104-
Optional name used to identify this Panel instance in logs and Sentry error reports.</br>
105-
Useful when running multiple Panel instances against the same database.
106-
schema:
107-
type: string
108-
default: ""
109-
- variable: sentry_url
110-
label: Sentry DSN
111-
description: |
112-
Optional Sentry DSN for error reporting.</br>
113-
Format: https://&lt;public_key&gt;@sentry.io/&lt;project_id&gt;</br>
114-
Leave empty to disable Sentry integration.
115-
schema:
116-
type: string
117-
default: ""
118-
private: true
11968
- variable: trusted_proxies
12069
label: Trusted Proxies
12170
description: |
@@ -251,15 +200,6 @@ questions:
251200
required: true
252201
$ref:
253202
- definitions/node_bind_ip
254-
- variable: host_network
255-
label: Host Network
256-
description: |
257-
Bind to the host network.</br>
258-
This will give the container direct access to the host's network interfaces.</br>
259-
Required for some Wings allocations setups where game servers must be reachable on a wide range of host ports.
260-
schema:
261-
type: boolean
262-
default: false
263203

264204
- variable: storage
265205
label: ""
@@ -423,6 +363,93 @@ questions:
423363
show_if: [["acl_enable", "=", false]]
424364
required: true
425365

366+
- variable: wings_data
367+
label: Wings Data Storage
368+
description: |
369+
Stores all data managed by the integrated Wings instance: game server volumes,
370+
backups, archives, runtime mounts, working files and logs.</br>
371+
</br>
372+
This dataset is mounted into the Calagopus container at the same path it has
373+
on the host. This is required because Wings spawns sibling containers via the
374+
Docker socket, and those sibling containers bind-mount paths under this dataset
375+
directly from the host - so the host path and the container path must match.</br>
376+
</br>
377+
Pick a dataset on the pool where you want game server data to live. Server
378+
volumes can grow large, so plan accordingly.
379+
schema:
380+
type: dict
381+
attrs:
382+
- variable: type
383+
label: Type
384+
description: |
385+
ixVolume: Is dataset created automatically by the system.</br>
386+
Host Path: Is a path that already exists on the system.
387+
schema:
388+
type: string
389+
required: true
390+
default: "ix_volume"
391+
enum:
392+
- value: "host_path"
393+
description: Host Path (Path that already exists on the system)
394+
- value: "ix_volume"
395+
description: ixVolume (Dataset created automatically by the system)
396+
- variable: ix_volume_config
397+
label: ixVolume Configuration
398+
description: The configuration for the ixVolume dataset.
399+
schema:
400+
type: dict
401+
show_if: [["type", "=", "ix_volume"]]
402+
$ref:
403+
- "normalize/ix_volume"
404+
attrs:
405+
- variable: acl_enable
406+
label: Enable ACL
407+
description: Enable ACL for the storage.
408+
schema:
409+
type: boolean
410+
default: false
411+
- variable: dataset_name
412+
label: Dataset Name
413+
description: The name of the dataset to use for storage.
414+
schema:
415+
type: string
416+
required: true
417+
hidden: true
418+
default: "wings_data"
419+
- variable: acl_entries
420+
label: ACL Configuration
421+
schema:
422+
type: dict
423+
show_if: [["acl_enable", "=", true]]
424+
attrs: []
425+
- variable: host_path_config
426+
label: Host Path Configuration
427+
schema:
428+
type: dict
429+
show_if: [["type", "=", "host_path"]]
430+
attrs:
431+
- variable: acl_enable
432+
label: Enable ACL
433+
description: Enable ACL for the storage.
434+
schema:
435+
type: boolean
436+
default: false
437+
- variable: acl
438+
label: ACL Configuration
439+
schema:
440+
type: dict
441+
show_if: [["acl_enable", "=", true]]
442+
attrs: []
443+
$ref:
444+
- "normalize/acl"
445+
- variable: path
446+
label: Host Path
447+
description: The host path to use for storage.
448+
schema:
449+
type: hostpath
450+
show_if: [["acl_enable", "=", false]]
451+
required: true
452+
426453
- variable: postgres_data
427454
label: PostgreSQL Data Storage
428455
description: |

ix-dev/community/calagopus/templates/docker-compose.yaml

Lines changed: 27 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,31 @@
11
{% set tpl = ix_lib.base.render.Render(values) %}
22

33
{% set calagopus_net = tpl.networks.create_internal("calagopus-net") %}
4+
{% set wings_host_path = tpl.funcs.get_host_path(values.storage.wings_data) %}
45

56
{# Main Calagopus AIO container (Panel + Wings) #}
67
{% set c1 = tpl.add_container(values.consts.calagopus_container_name, "image") %}
78
{% do c1.add_network(calagopus_net) %}
89

910
{% set wings_init = tpl.add_container(values.consts.wings_init_container_name, "image") %}
1011
{% do wings_init.setup_as_helper() %}
11-
{% do wings_init.set_user(0, 0) %}
1212
{% do wings_init.set_entrypoint(["/bin/sh", "-c"]) %}
13-
{% do wings_init.set_command(["[ -s %s ] || echo '{}' > %s" | format(values.consts.wings_config_path, values.consts.wings_config_path)]) %}
13+
{% set wings_seed_yaml -%}
14+
system:
15+
root_directory: {{ wings_host_path }}
16+
log_directory: {{ wings_host_path }}/logs
17+
data_directory: {{ wings_host_path }}/volumes
18+
vmount_directory: {{ wings_host_path }}/vmounts
19+
archive_directory: {{ wings_host_path }}/archives
20+
backup_directory: {{ wings_host_path }}/backups
21+
tmp_directory: {{ wings_host_path }}/tmp
22+
{%- endset %}
23+
{% do wings_init.set_command([
24+
"[ -s " ~ values.consts.wings_config_path ~ " ] || cat > " ~ values.consts.wings_config_path ~ " <<'EOF'\n" ~ wings_seed_yaml ~ "\nEOF"
25+
]) %}
1426
{% do wings_init.add_storage(values.consts.data_path, values.storage.data) %}
1527

16-
{# Permissions init container - prepares storage for postgres and the panel data dir #}
28+
{# Permissions init container - prepares storage for postgres and the redis temp volume #}
1729
{% set perm_container = tpl.deps.perms(values.consts.perms_container_name) %}
1830

1931
{# PostgreSQL dependency #}
@@ -34,31 +46,27 @@
3446
{% set redis = tpl.deps.redis(values.consts.redis_container_name, "redis_image", redis_config, perm_container) %}
3547
{% do redis.container.add_network(calagopus_net) %}
3648

37-
{# AIO image manages sibling containers via the docker socket and therefore needs to run as root #}
38-
{% do c1.set_user(0, 0) %}
39-
{% do c1.add_caps(["CHOWN", "DAC_OVERRIDE", "FOWNER", "KILL", "SETFCAP", "SETGID", "SETPCAP", "SETUID"]) %}
40-
4149
{# Health check on the panel HTTP port #}
42-
{% do c1.healthcheck.set_test("netcat", {"port": values.consts.internal_web_port}) %}
50+
{% do c1.healthcheck.set_test("netcat", {"port": values.network.web_port.port_number}) %}
4351

4452
{# Dependencies must come up healthy before the panel starts #}
4553
{% do c1.depends.add_dependency(values.consts.postgres_container_name, "service_healthy") %}
4654
{% do c1.depends.add_dependency(values.consts.redis_container_name, "service_healthy") %}
4755
{% do c1.depends.add_dependency(values.consts.wings_init_container_name, "service_completed_successfully") %}
4856

4957
{# Core environment - mirrors the upstream calagopus compose.yml #}
50-
{% do c1.environment.add_env("PORT", values.consts.internal_web_port) %}
58+
{% do c1.environment.add_env("PORT", values.network.web_port.port_number) %}
5159
{% do c1.environment.add_env("BIND", "0.0.0.0") %}
5260
{% do c1.environment.add_env("DATABASE_URL", postgres.get_url("postgresql")) %}
5361
{% do c1.environment.add_env("DATABASE_MIGRATE", "true") %}
5462
{% do c1.environment.add_env("REDIS_MODE", "redis") %}
5563
{% do c1.environment.add_env("REDIS_URL", redis.get_url("redis")) %}
5664
{% do c1.environment.add_env("APP_ENCRYPTION_KEY", values.calagopus.encryption_key) %}
57-
{% do c1.environment.add_env("APP_PRIMARY", values.calagopus.app_primary) %}
58-
{% do c1.environment.add_env("APP_DEBUG", values.calagopus.app_debug) %}
65+
{% do c1.environment.add_env("APP_PRIMARY", "true") %}
66+
{% do c1.environment.add_env("APP_DEBUG", "false") %}
5967
{% do c1.environment.add_env("APP_ENABLE_WINGS_PROXY", values.calagopus.enable_wings_proxy) %}
60-
{% do c1.environment.add_env("APP_USE_DECRYPTION_CACHE", values.calagopus.use_decryption_cache) %}
61-
{% do c1.environment.add_env("APP_USE_INTERNAL_CACHE", values.calagopus.use_internal_cache) %}
68+
{% do c1.environment.add_env("APP_USE_DECRYPTION_CACHE", "true") %}
69+
{% do c1.environment.add_env("APP_USE_INTERNAL_CACHE", "true") %}
6270
{% do c1.environment.add_env("APP_LOG_DIRECTORY", values.consts.log_path) %}
6371

6472
{# AIO-specific: where the integrated wings reads its bootstrap configuration from.
@@ -69,45 +77,23 @@
6977
{% do c1.environment.add_env("APP_TRUSTED_PROXIES", values.calagopus.trusted_proxies | join(",")) %}
7078
{% endif %}
7179

72-
{% if values.calagopus.server_name %}
73-
{% do c1.environment.add_env("SERVER_NAME", values.calagopus.server_name) %}
74-
{% endif %}
75-
76-
{% if values.calagopus.sentry_url %}
77-
{% do c1.environment.add_env("SENTRY_URL", values.calagopus.sentry_url) %}
78-
{% endif %}
79-
8080
{% do c1.environment.add_user_envs(values.calagopus.additional_envs) %}
8181

8282
{# Ports - panel HTTP and Wings SFTP #}
83-
{% if not values.network.host_network %}
84-
{% do c1.add_port(values.network.web_port, {"container_port": values.consts.internal_web_port}) %}
85-
{% do c1.add_port(values.network.sftp_port, {"container_port": values.consts.internal_sftp_port}) %}
86-
{% endif %}
83+
{% do c1.add_port(values.network.web_port, {"container_port": values.consts.internal_web_port}) %}
84+
{% do c1.add_port(values.network.sftp_port, {"container_port": values.consts.internal_sftp_port}) %}
8785

8886
{# Panel-managed storage (configurable via questions.yaml) #}
8987
{% do c1.add_storage(values.consts.data_path, values.storage.data) %}
9088
{% do c1.add_storage(values.consts.log_path, values.storage.logs) %}
9189

90+
{# Wings data: same host path inside the container as on the host so sibling containers
91+
spawned via the docker socket can bind-mount paths under it. #}
92+
{% do c1.add_storage(wings_host_path, values.storage.wings_data) %}
93+
9294
{# Docker socket - required for Wings to manage game server containers #}
9395
{% do c1.add_docker_socket(read_only=False) %}
9496

95-
{# Wings host bind mounts. These paths MUST be identical on host and container because
96-
Wings spawns sibling containers (via the docker socket) that reference these host
97-
paths directly. They are intentionally not exposed in questions.yaml. #}
98-
{% set wings_host_paths = [
99-
(values.consts.wings_etc_path, values.consts.wings_etc_path, false),
100-
(values.consts.wings_data_path, values.consts.wings_data_path, false),
101-
(values.consts.wings_tmp_path, values.consts.wings_tmp_path, false),
102-
] %}
103-
{% for host_path, container_path, read_only in wings_host_paths %}
104-
{% do c1.add_storage(container_path, {
105-
"type": "host_path",
106-
"read_only": read_only,
107-
"host_path_config": {"path": host_path, "create_host_path": true},
108-
}) %}
109-
{% endfor %}
110-
11197
{# User-defined extra storage #}
11298
{% for store in values.storage.additional_storage %}
11399
{% do c1.add_storage(store.mount_path, store) %}

0 commit comments

Comments
 (0)