Part 1 of Any language on Flisk.
docs/byo.md does not list allowed languages. The heading is "Bringing a framework app (Node, Go, Rust, ...)" and the next sentence is the rule: any app that fits the contract can run as a bring-your-own resource. Fastify is the worked example only because it needs nothing beyond the framework. The provider guide sends Java, Ruby, and a legacy service through the same door. Goose goes through it too.
--type backend --language is a different door. That one scaffolds Bun, or Python, Go, Rust, and Node if you ask. It is a generator. BYO is not a generator. Flisk does not write your application. You bring the image. The language inside the image is yours.
Same command, any language
flisk resource <name> --type bring-your-own --stack <stack> \
--dockerfile ./Dockerfile --health-path /health \
--path services/<stack>/<name> --yes
byo is the same type as bring-your-own. Fastify, Gin, Flask, Quarkus, Phoenix, and a Goose binary all use that line. What changes is the directory you point --path at.
The only contract
The container has to do four things. The language does not change them.
- Listen on the port in
service.json. Compose setsPORTto that value. It also setsDATABASE_URLin every BYO container. ReadPORT. Do not hardcode it. - Bind
0.0.0.0. Traefik is on the Docker network. - Answer HTTP 200 on
healthCheckPathif it speaks HTTP.--health-path /healthis the usual value. Flisk does not add a Composecurlprobe for this type, so the image does not needcurl. A non-HTTP process can omithealthCheckPath. - Provide the image with a Dockerfile or
--image.COPYpaths are relative to the resource directory unless you setbuildContext.
Ports are 4000 through 5999. --port must be unused in that range. Omit it and Flisk picks the next free one.
What "any language" does not mean
It does not mean Flisk writes a Goose scaffold, a health route, or a PORT read. The limits section says that outright: no generated code. If you run the command with no Dockerfile, Flisk writes an Nginx placeholder that listens on the assigned port. If a Dockerfile already exists, Flisk keeps it and does not overwrite it. Bring yours first.
It also does not mean live reload. Only the manifest is watched. After a source change, rebuild the image from the Tilt UI or restart flisk up. A native Go service can opt into dev.liveReload. A Go app you bring as a container, including the Gin example, does not get that loop. Goose will not either.
Part 2 is a Goose app that meets the same four rules as the Fastify server.js in the doc.
Top comments (2)
The distinction between scaffolding a language (--type backend --language) and providing a runtime contract (--type bring-your-own) is a really clean design choice. It prevents the classic "generator bloat" problem where a CLI tries to support every framework under the sun and inevitably does them all poorly. The four rules for the container are basically the 12-factor app manifest boiled down to its absolute essentials for a Docker network.
Since you lose live reload for BYO apps (which makes total sense for non-native runtimes), how does the Tilt UI handle the rebuild latency? I imagine bringing a heavy Quarkus or Rust binary through this door takes significantly longer to rebuild from source than a native Go service. Do you have any dependency caching strategies in place, or is it just expected that the dev embraces the slower iteration cycle in exchange for total language freedom?
great comment, we will add it github.com/flisk-sh/flisk/issues/1917