Stack
Each stack is a fully independent clone (own ports, own Compose project) — create one per app you want running concurrently.
Configure app
Create a new app in the active stack, or update its config and regenerate.
Secrets
Secrets are isolated to the selected stack and stored in its ignored servicelibs/service/secrets.env.
Values are masked by default. Select “View” beside a secret to reveal it for the selected stack.
| Secret | Value | Action |
|---|
Environment
Inspect non-secret environment layers for a service, including shared defaults, service-local values, and per-stack application settings. Manage credentials only in Secrets.
| Variable | Value | Source file |
|---|
Configure services
Register additional text configuration files from a built service image. Backendgen inspects the image without starting its application and mounts an editable stack-owned copy on the next service recreation.
Database migration
Migration rules belong to the selected stack. Saving rules does not copy data; preview the plan before running a migration.
Databases and collections
Choose what to migrate for each collection. You can also skip an entire database.
Review and run
Preview reads the source and destination only. Starting applies the previewed rules; copied collections replace their destination after the full copy succeeds.
Run a preview to inspect source and destination counts before starting.
Deploy
Bring the active stack's containers up or down and check what's running.
| Service | Type | Status | Actions |
|---|
Create service bundle
Add an app container to the selected stack. Anyone with access to this stack can add a service bundle.
Existing service bundles
Edit the private working copy, run it locally on the stack network, and promote it to shared Compose only when you are ready.
Local test run
Private test containers are attached to this stack’s backend network and reachable only from this computer.
Build output
Output from the latest private image build. Restart rebuilds saved files before launching.
No private build has run yet.
Local run logs
Recent output from this bundle’s private container.
Select a bundle and start its local run to see logs.
Bundle a service
New bundles are saved and built as private test images. A local run joins the selected stack’s backend_network and can call built-in services by Docker DNS (for example auth-gateway:5000); it publishes only a random port on 127.0.0.1. It does not enter shared Compose or get a Caddy route until an administrator promotes it.
Bundle layout and working example
Upload a gzip-compressed tar archive with a Dockerfile at its root. Example folder before archiving:
my-service/
├── Dockerfile
├── package.json
└── server.js
Dockerfile (the port must match “Internal HTTP port”):
FROM node:20-alpine
WORKDIR /app
COPY package.json server.js ./
ENV PORT=3000
EXPOSE 3000
CMD ["npm", "start"]
package.json:
{
"name": "my-service",
"version": "1.0.0",
"private": true,
"scripts": { "start": "node server.js" }
}
server.js (listen on 0.0.0.0, not localhost):
const http = require("node:http");
const port = Number(process.env.PORT || 3000);
http.createServer((req, res) => {
res.writeHead(200, { "content-type": "application/json" });
res.end(JSON.stringify({ service: "my-service", status: "ok" }));
}).listen(port, "0.0.0.0", () => {
console.log(`my-service listening on ${port}`);
});
Archive the contents of that folder: tar -czf my-service.tar.gz -C my-service .. The private test container loads runtime variables from the bundle’s private .env (edit them in Environment) and publishes only a random localhost port. Do not include .env or credentials in the archive.
Generated Compose shape after promotion (you do not upload this file):
services:
custom-my-service:
build: ./custom-services/.promoted/my-service
env_file: ./custom-services/my-service/.env
expose: ["3000"]
read_only: true
tmpfs: ["/tmp:rw,noexec,nosuid,size=64m"]
security_opt: ["no-new-privileges:true"]
cap_drop: [ALL]
mem_limit: 512m
cpus: 0.5
pids_limit: 128
networks: [backend_network]
networks: {}
The private test container can reach built-in services on backend_network, but credentials are not copied into it automatically. Only run code you trust. When promoted, the service is shared in Compose and its web route can be reached by anyone who can reach the stack’s Caddy URL.
Caddy removes the /extensions/my-service prefix before forwarding, so your app receives requests at /. Use relative links/assets or configure your frontend with the add-on’s base path.
The container filesystem is read-only except for temporary /tmp; write durable data to a backend service instead. The runtime is capped at 512 MB memory, 0.5 CPU, and 128 processes.
Users & stack access
Create accounts and grant access to one or more stacks. Administrators automatically have access to every stack.
Smoke test
Run a quick pass/fail check against a running stack.