Replace server-side Debian package deployment with a container-based deployment strategy built around Docker Compose, while keeping the device client as a native host-installed package distributed with GoReleaser.
This migration also incorporates the final product rename to Nixstasis.
This plan intentionally defers Kubernetes to a future change.
packages/clientremains native-distributed.packages/clientmigrates to GoReleaser.packages/clientwill bundle a pinnedfrpcbinary.- The bundled
frpcwill be installed to a private path:/usr/libexec/nixstasis/frpc. packages/server,packages/caddy, andpackages/frpwill no longer target Debian package delivery.- Server-side deployment target for this change is Docker Compose only.
- Caddy remains a hard requirement because it provides:
- on-demand TLS
- OIDC authentication via AuthCrunch
- PostgreSQL will be included in the Compose stack by default.
- PostgreSQL configuration should still allow later use of an external database without changing application code.
frpsshould track a pinned upstream version using the safest available option:- preferred: mirrored internal image pinned by digest
- fallback: pinned upstream image by digest
The packaging and deployment migration should be used as the point where the externally visible product name becomes
Nixstasis.
The rename should be treated as part of the same workstream so package names, binary names, service names, config paths, container names, image names, and documentation all converge on the new name together.
- Product name:
Nixstasis - Old placeholder name to phase out entirely from the repository
- Future packaging, runtime, and deployment assets should prefer
nixstasisfor filesystem paths and identifiers.
- binary name:
nixstasis - bundled private runtime path:
/usr/libexec/nixstasis/ - config directory:
/etc/nixstasis/ - persistent/runtime-owned data should move under a
nixstasisnamespace where practical - service/container/image names should use
nixstasis
Do not preserve any old placeholder naming in newly created packaging or deployment assets unless there is a specific compatibility requirement. Any compatibility shims, if needed later, should be explicit and temporary.
- Built and released with GoReleaser.
- Produces Linux archives,
.deb, and.rpmartifacts. - Installs:
nixstasisas a user-facing commandfrpcunder/usr/libexec/nixstasis/frpc- config templates and systemd units
- Does not depend on a separately installed
frppackage.
- Deployed with Docker Compose.
- Compose stack includes:
postgresnixstasisfrpscaddy
- Caddy is the required public ingress and authentication layer.
- Phoenix is run as a release, not via Mix in production.
- Migrations are run explicitly, not implicitly on app startup.
- Add a Docker Compose deployment layout and supporting configuration files.
- Add OCI image build support for server-side components.
- Migrate client packaging to GoReleaser.
- Bundle
frpcinto client artifacts. - Update CI/CD direction to support container delivery for server-side components and GoReleaser for the client.
- Kubernetes manifests or Helm charts.
- Nix flake support for the client.
- Full cleanup of every legacy packaging artifact in the same change, unless necessary to avoid confusion.
Before implementation, normalize these mismatches so the new deployment path has a clear runtime contract:
-
Canonical internal app port behind Caddy.
- Current repo evidence suggests
4000in Phoenix runtime and8000in some Caddy assumptions.
- Current repo evidence suggests
-
Canonical on-demand TLS approval endpoint.
- Current repo/PoC history references both
/permit_tlsand/api/v1/check_domain.
- Current repo/PoC history references both
-
Canonical domain naming rules.
- Current configs and controller logic need one source of truth.
-
Canonical env var names and secret ownership.
- Caddy/AuthCrunch secrets, Phoenix secrets, and DB credentials must be clearly separated.
-
frpsversion ownership and image source.
Define and document these values before wiring Compose:
NIXSTASIS_PORTor fixed internal app portDATABASE_URLor DB host/user/password/database splitSECRET_KEY_BASEPHX_HOST- Caddy/AuthCrunch env vars:
CLIENT_IDCLIENT_SECRETTENANT_IDJWT_KEY
frpsports:- bind/control port
- HTTP tunnel port
- dashboard port
- SSH/tcpmux port
- canonical allowed domain patterns
- canonical TLS approval endpoint path
Add a new deployment source of truth under deploy/compose/.
Planned files:
deploy/compose/docker-compose.ymldeploy/compose/.env.exampledeploy/compose/README.mddeploy/compose/caddy/Caddyfiledeploy/compose/frps/frps.toml- optional helper files:
deploy/compose/env/deploy/compose/scripts/
Compose responsibilities:
-
postgres- default included service
- named volume for persistence
- env-driven credentials
-
nixstasis- Phoenix release container
- depends on DB availability
- receives runtime env
- internal only, fronted by Caddy
-
frps- pinned image reference
- mounted
frps.toml - explicit published ports as required
-
caddy- custom AuthCrunch-enabled image
- mounted
Caddyfile - published public HTTP/HTTPS ports
- public edge for auth and TLS
Add container build support for packages/server.
Planned files:
packages/server/Dockerfile- optional supporting files:
packages/server/.dockerignorepackages/server/bin/docker-entrypoint.shif needed
Implementation expectations:
- multi-stage build
- build Phoenix assets
- build Phoenix release
- runtime image starts the release via the renamed release command
- provide a documented explicit migration command via
bin/migrate
Server image responsibilities:
- carry release artifacts only
- not carry systemd or Debian package semantics
- not assume host paths like
/etc/caddyor/etc/frp
Add OCI image build support for packages/caddy.
Planned files:
packages/caddy/Dockerfile- optional refactor of existing build helper scripts if they remain useful
Implementation expectations:
- use
xcaddy - include AuthCrunch plugin
- runtime image should be minimal
- secrets/config should be injected at runtime, not baked into the image
Do not continue server-side Debian packaging for frps.
Expected implementation:
- configure Compose to use a pinned image reference for
frps - prefer mirrored internal image pinned by digest
- keep repo-managed
frps.tomlunder deployment config
Repo responsibilities:
- preserve
frps.tomlas tracked config - document the exact image pinning policy
Migrate packages/client from the current package workflow to GoReleaser.
Planned files:
packages/client/.goreleaser.yaml- optional helper scripts:
packages/client/scripts/release/packages/client/scripts/fetch_frpc.sh
Implementation expectations:
- build
nixstasisfor supported Linux targets - produce archives
- produce
.deb - produce
.rpm - bundle
frpcfor each target architecture - install bundled
frpcunder/usr/libexec/nixstasis/frpc
Client packaging responsibilities:
- install:
nixstasis- bundled
frpc - config templates
- systemd units
- remove dependency on a separately installed
frppackage - keep client host integration intact
Update client runtime code to launch bundled frpc from a private path.
Expected code changes:
- replace
frpcPATH lookup assumptions with an explicit configured/bundled path - define one canonical default path for packaged installs:
/usr/libexec/nixstasis/frpc
- allow override only if there is a clear config requirement
- update default config paths and any service/runtime path assumptions from
nixstasistonixstasis
Related areas likely to change:
packages/client/internal/frp/manager.gopackages/client/cmd/nixstasis/poll.go- client config defaults and docs
- command and path definitions that currently embed
nixstasis
Add OCI image workflows for:
packages/serverpackages/caddy
Potential workflow files:
.github/workflows/build_server_image.yml.github/workflows/build_caddy_image.yml- or a single shared image workflow with matrix-based inputs
Expected behavior:
- build image
- tag image deterministically
- push to registry
- optionally publish semver tags on release
Add a GoReleaser workflow for packages/client.
Potential workflow file:
.github/workflows/release_client.yml
Expected behavior:
- run tests/lint as needed
- fetch pinned
frpcartifacts per target - run GoReleaser
- publish client release assets
The following should be treated as migration leftovers and eventually removed or restricted to client-only use:
.github/workflows/build_package.yml.github/workflows/publish_package.yml- package-based server/caddy/frp release logic
- Choose one internal app port.
- Choose one TLS approval endpoint.
- Choose one set of domain rules.
- Document required env vars and secrets.
- Decide image source and pinning policy for
frps.
Output of this phase:
- updated docs and config templates that represent the chosen runtime contract
- Create
deploy/compose/docker-compose.yml. - Create
deploy/compose/.env.example. - Add compose-specific
Caddyfile. - Add compose-specific
frps.toml. - Add deployment README with first-run instructions.
- Use
nixstasisnaming consistently in service names, image tags, and docs.
Output of this phase:
- one documented single-host deployment flow
- Add
packages/server/Dockerfile. - Build Phoenix assets and release in image build.
- Rename release/runtime-facing identifiers from
nixstasistonixstasis. - Validate startup via the renamed release command.
- Validate migrations via explicit
bin/migratecommand.
Output of this phase:
- runnable
nixstasisimage used by Compose
- Add
packages/caddy/Dockerfile. - Build Caddy with AuthCrunch.
- Validate runtime config injection.
- Validate auth flow and on-demand TLS configuration path.
Output of this phase:
- runnable
caddyimage used by Compose
- Pin the chosen
frpsimage. - Mount
frps.toml. - Validate the required tunnel/dashboard ports.
- Validate connectivity from Caddy and client assumptions.
Output of this phase:
- runnable
frpsservice in Compose
- Add
.goreleaser.yaml. - Add
frpcfetch/bundle step. - Rename package-owned paths and user-facing command names from
nixstasistonixstasis. - Update package layout to install bundled
frpcinlibexec. - Update client runtime to invoke bundled path.
- Generate
.deband.rpmartifacts.
Output of this phase:
- client-native release pipeline independent of the old package action
- Update docs to make Compose the primary deployment path.
- Stop documenting server/caddy/frp Debian packaging as supported deployment output.
- Restrict or remove old package workflows for non-client packages.
Output of this phase:
- clear separation between client native packaging and server-side container deployment
These should not be used as the future source of truth for server-side deployment:
packages/server/package_options.ymlpackages/server/build/root-dir/**packages/server/build/pre_package.sh- server-side
DEBIAN/* - server-side systemd service templates
- server-side package-based wiring for Caddy and FRP
packages/caddy/package_options.ymlpackages/frp/package_options.yml
They may remain temporarily during transition, but the new source of truth should become:
deploy/compose/**- container Dockerfiles
- image build workflows
- client GoReleaser config
Update these areas as implementation lands:
- root
README.md packages/client/README.mdpackages/server/README.md- any packaging/deployment references that still describe server-side Debian delivery as current
- any docs that still describe the product as
Nixstasis - release, package, service, and command documentation that still uses
nixstasispaths or identifiers
The migration is not complete until all of the following are true:
docker compose upbrings up:postgresnixstasisfrpscaddy
- Phoenix is reachable only through Caddy in the intended deployment path.
- Caddy is using the configured AuthCrunch/OIDC flow.
- Caddy can reach the chosen TLS approval endpoint.
frpsis reachable on the required ports.- client packages install bundled
frpcin/usr/libexec/nixstasis/frpc. - client runtime launches bundled
frpcsuccessfully. - client release artifacts are produced by GoReleaser as
.deband.rpm. - server-side release docs no longer point users toward Debian package deployment.
- user-facing package, command, service, and path names reflect
Nixstasisrather thanNixstasis.
The first code change should be to normalize the runtime contract and add deploy/compose/ as the new deployment
source of truth. That creates a stable base for the server image, Caddy image, and later client packaging migration.