Import the services in a repo's root docker-compose.yml into Flisk as service.json files, or register the repo's own Tiltfile so flisk up runs it. This is the Compose first tier of flisk-sh/flisk#511.
flisk import . --dry-run # plan only
flisk import . # plan, then ask before writing
flisk import . --yes # write service.json files and project setup- Reads one project: the root
docker-compose.yml, orcompose.yaml,compose.ymlordocker-compose.yamlwhen that is absent, throughdocker compose config --no-interpolate --no-env-resolution --no-path-resolutionwhen docker compose is installed, soinclude:,extendsand anchors are resolved asdocker compose upresolves them (wger keeps db, cache and powersync inservices/*.yaml),${VAR}templates stay as written, and an included file's paths stay relative to its own folder. Without docker compose (or withFLISK_IMPORT_DOCKER=0) the file is read as YAML, and the plan says when that leavesincludeorextendsunresolved. Not override files, Dockerfiles on their own,package.json, or Procfiles. - Writes: one bring-your-own
service.jsonper Compose service, plus.flisk/project.jsonif the repo is not a Flisk project yet. It never edits the Compose file or overwrites aservice.jsonwithout--force. - Keeps the install's data: each named volume gets
name, the Docker volumedocker compose upcreated (<project>_<volume>, its ownname:, or anexternalvolume), soflisk upmounts the same data with no copy (needs the Flisk release with flisk#2964). - Keeps:
profiles(the service starts only withflisk up --profile),env_fileasenvFile,tmpfsanddeploy.resources.limits(container.tmpfs,cpus,memory). - Keeps (Flisk 1.4.28): each
depends_oncondition as a{ name, condition }entry (the list form asservice_started),build.argsasbuildArgs,logging(options as strings), bind paths from the environment (${OM_AGENTS_DIR:-./agents}, the default rewritten relative toservice.json), SELinux labels (:z,:Z,bind.selinux) asselinux, and images with${NAME:?message}. A bind path from a variable without a default (${STORAGE_DIR}/redis) andextra_hostswith a variable are still not carried over. - Keeps:
stop_grace_period(container.stopGracePeriod, such as30s), thealiasesof every network (container.networkAliases),extra_hostsin list or map form (container.extraHosts, such ashost.docker.internal:host-gateway) andulimits(container.ulimits). A service renamed to kebab-case (lychee_apibecomeslychee-api) keeps its Compose name as a network alias, sohttp://lychee_api:8000in another service still resolves.container_nameis dropped with a note: Flisk names containers itself. These fields, an image with variables andon-failure:Nneed the Flisk core release that reads them; an older core does not apply them. - Keeps: the service names (
dbstaysdb, so it stays the hostname), the build context and Dockerfile (or the image), the container port as written (3000, 80, 5432),depends_on, and an HTTP health path. Databases and brokers are private, with no proxy route. - Runtime keys: named volumes and bind mounts inside the project (
volumes),cap_add,cap_drop,init,read_only,user,security_optandshm_size(container), extra published ports (extraPorts, on the same host address as in Compose:"80:80"stays on every interface (written aspublish: "80", so IPv6 is kept;"0.0.0.0:80:80"isbind),"127.0.0.1:5432:5432"on loopback, and another address or a${VAR:-default}or${VAR-default}host side aspublish),network_mode: service:<name>(sidecarOf) andrestart. - Environment: the committed
service.jsonenvgets every pure Compose expression as written, whatever its name (${DB_PASSWORD:-password},${PORT-8080},${A:-${B:-file}}), so a fresh clone keeps the defaults, and every literal (sqlite,worker,lychee_db,http://invidious:3000) unless its name looks like a credential (PASSWORD,TOKEN,KEY, ...) or the value holds one (a URL with a user or password, a token query parameter, a long random string, a PEM block). Those go intosecrets.required, with its literal value copied to the project.env, which is added to.gitignore. Values in.envalready are kept. - Round trip: a routed port that Compose also publishes (
"${APP_PORT:-8000}:8000") stays inextraPortstoo, and ahealthcheckstays ashealthCheck.command(withinterval,timeout,retriesandstart_periodasstartPeriod) even when an HTTP path was read, soflisk-export composewrites a file thatdocker composestarts the same way (LycheeOrg/Lychee round-trips with no difference but names and aliases). A service with norestartkeeps none, so Flisk runs it with its defaultunless-stopped(unlike Compose, Flisk then restarts it after a crash or a reboot; the plan says so).restart: "no"is written only when Compose sets it or another service waits for this one withcondition: service_completed_successfully, because Flisk runs arestart: "no"service as a one-shot job and waits for it to exit,healthcheck: { disable: true }ortest: ["NONE"]becomeshealthCheck: false, a value that mixes${VAR}with literal text (${SERVER_NAME:-localhost}, php:80) stays inenvunless it could hold a credential,*_FILEpaths (POSTGRES_PASSWORD_FILE=/run/secrets/pg) stay inenv, and the Compose project name (name:or the folder's, such aspl_mail) is recorded ascomposeProjectNames.<stack>in.flisk/project.json, soflisk-export composekeeps the container names. - A tracked
.env: a repo that commits its.env(Symfony) keeps it as it is, and it is not added to.gitignore; values that must stay local go to.env.localwhen git already ignores it, else to.flisk/.env.import(added to.gitignore), and each service that needs them reads that file asenvFile. - Keeps: a
commandorentrypointascontainer.command/container.entrypoint(Flisk 1.4.31; a list as it is, a string split into exec-form arguments as Compose splits it, with no shell, socommand: server /exportonminio/miniostays the arguments of itsENTRYPOINT;$$and${VAR}are written as they are, because Flisk puts them into a Compose file again; only a string with unbalanced quotes runs throughsh -c, and the plan says so; an empty argument is refused by Flisk and not written; the same for a service withbuild:), ahealthcheckashealthCheck.command, a published database port asextraPorts, andrestart: on-failure:Nwith its retry limit (N from 1 to 100). - Drops, and says so:
commandon a service with abuild,env_fileand every other untranslated key; anonymous volumes and paths outside the project;network_modeother thanservice:<name>; services with invalid profile names,extends, an out-of-tree build, or an image with characters or variable forms other than${NAME},${NAME:-default}and${NAME-default}(postgres:${POSTGRES_VERSION:-18}-alpineis written as it is). - Does not start anything. Run
flisk upwith Flisk core 1.4.31 or later (container.entrypointneeds it; dependsOn conditions, buildArgs, logging, bind paths from the environment, SELinux labels and${NAME:?message}images need 1.4.28). The importer does not check the core version. - Exit codes:
0when the plan succeeds (--dry-run, files written, or allservice.jsonfiles already exist),1when no rootdocker-compose.ymlor mappable service is found, and2when the write prompt is declined or closed without an answer (Nothing written.).
See docs/import.md for the full mapping and limits. flisk import is the core CLI command backed by this package.
A repo that already runs on Tilt keeps its own Tiltfile. flisk-import tiltfile registers it in .flisk/project.json, so flisk up runs it unchanged.
flisk import tiltfile --dry-run # plan only
flisk import tiltfile # plan, then ask before writing
flisk import tiltfile dev/Tiltfile --yes # a named file, or a folder that holds a Tiltfile- Path:
[path]defaults toTiltfile; a folder means<folder>/Tiltfile. It is resolved from the current directory. The project root is the nearest folder with.flisk/project.json, else the current directory. - Writes: only
.flisk/project.json, with"orchestration": { "source": "tiltfile", "path": "<path from the project root>" }. Every other key is kept. Without aproject.json, it creates the same minimal one as the Compose import, with only the proxy stack. It never edits the Tiltfile. - Refuses (exit 1): a missing Tiltfile, a path inside
.flisk/(also through a symlink), a path with a quote or control character, aproject.jsonthat is not a JSON object or lacks the fieldsflisk upneeds, and aproject.jsonthat already setsorchestration(pass--forceto replace it). - Exit codes: the same as the Compose import:
0for--dry-runor a write,1for a refusal,2when the prompt is declined or closed (Nothing written.). - Needs a Flisk core that runs an imported Tiltfile (flisk#2849). The importer does not check the core version.
Plain flisk-import [dir] is still the Compose import. To import Compose from a folder named tiltfile, write flisk-import ./tiltfile.
bun install && bun run build && bun run testflisk-import is published to npmjs.org by the workflow in
.github/workflows/publish-npm.yml, triggered by a
GitHub release. It uses npm trusted publishing (OIDC), so no long-lived npm token
lives in this repository. The package is published under two names: the unscoped
flisk-import and the scoped @flisk-sh/flisk-import (the same archive with the name
rewritten in the packed manifest).
A trusted publisher has to exist on npm before the first OIDC publish succeeds, and it is configured per package name:
- Open the package on npmjs.com → Settings → Trusted Publisher → GitHub Actions
- Fill in:
- Organization:
flisk-sh - Repository:
flisk-import - Workflow filename:
publish-npm.yml - Allowed actions:
npm publish
- Organization:
- Repeat for
@flisk-sh/flisk-importonce that package exists
The same thing from the command line, for either name:
npm trust github flisk-import --file publish-npm.yml --repo flisk-sh/flisk-import --allow-publish
npm trust list flisk-importA package's first version cannot be published through OIDC — npm has no trusted publisher for a name that does not exist yet. Version 0.2.2 was therefore published once with a token; every version after that publishes over OIDC from the release workflow.