Skip to content
melonfleet edited this page Sep 11, 2026 · 10 revisions

Images

Everything you can do with images lives in the Images section: pull them, build them from a Dockerfile, tag them, delete them, prune the unused ones, and run a container from one.

Getting an image: one form, two sources

+ in the Images toolbar opens New Image, and Source at the top picks how:

  • Pull — download one somebody else published.
  • Build — make one from a Dockerfile on this Mac.

They were two toolbar buttons and two screens. Both forms are short — pull is two fields, build is five — and neither filled the column it was given, so they share one screen with one information rail that follows whichever source you pick. The button in the corner is named for what it will do, Pull or Build, rather than a generic Create.

Pulling an image

Give it a reference — alpine:latest, docker.io/library/nginx:alpine, ghcr.io/example/webapp:1.4.2, or a digest-pinned name@sha256:…. A bare name works; the registry and latest are filled in the way the CLI fills them in.

Pulls are slow and Flotilla expects that: the deadline for a pull is 30 minutes, not the few seconds a list command gets. Progress is reported as the CLI reports it.

HTTPS, or HTTP for a development registry

Connect using picks the scheme, and it is HTTPS every time the form opens.

container 1.4.1 removed the old auto scheme that fell back to plaintext on its own, so a registry with no TLS — a loopback or LAN development registry — is unreachable unless you ask for HTTP. Choosing it warns you that image layers cross the network unencrypted.

It has to be an anonymous registry: container refuses to send credentials over HTTP even when you have asked for it, failing with "refusing insecure credential exchange". And the choice is not remembered — a stored "use HTTP" would apply to your next pull from Docker Hub too, which is the silent downgrade 1.4.1 got rid of.

Private registries are not supported yet. There is no registry-authentication surface, so a pull that needs a login will fail with the CLI's own error. If you have already authenticated container outside Flotilla, that credential is what gets used.

Building from a Dockerfile

Build an image from a Dockerfile… in the Images toolbar.

You pick a context directory with Choose…, name the tag — myapp:latest is the placeholder — and Flotilla runs the build. Also on the form:

  • Dockerfile path, defaulting to <context>/Dockerfile.
  • Target stage, for a multi-stage Dockerfile. Optional.
  • Platform, defaulting to linux/arm64.
  • Ignore the build cache, for when a cached layer is the problem.

Why the file panel matters

This is the one place Flotilla deliberately widens its own security boundary, and it is worth understanding because it is the model for how any future exception should look.

Flotilla's default mount policy is deny all host paths. A build has to read a directory on your Mac, so for that one command Flotilla builds a CLI scoped to exactly the directory you chose — and nothing else.

The file panel is the authorisation. You picking the folder is the grant. It is narrow, explicit, scoped to a single invocation, and named at the call site rather than inherited from a global setting somebody forgot about. The alternative — relaxing the shared policy — would have quietly widened bind mounts in the Run form too.

Two related details:

  • The context path is resolved through symlinks before use. Choosing a folder in a panel and having a link inside it silently redirect the build somewhere else is a real class of bug.
  • The context argument is required, even though the CLI defaults it to .. Defaulting it meant a build silently archived the process working directory — and a relative path cannot be checked against an absolute policy root anyway.

Tagging

A tag is a name pointing at an image, not a copy of it. Tagging docker.io/library/nginx:alpine as myapp:v1 gives you a second name for the same bytes — no disk is used, and deleting one name leaves the image reachable by the other.

It is worth doing when you want a short local name instead of a long registry path, or a stable name like myapp:current that you can re-point at a newer image later without changing anything that refers to it.

Tag… on a row, or from the overflow menu. Source and target are both validated as image references before anything runs.

Filtering a long image list

Images filter and sort by architecture / platform, size, age and label, and the row has a Copy submenu. All the usual column hiding applies — see Views and filtering.

Deleting and pruning

Delete removes one image. Delete images no container is using is the prune action — it is labelled by what it does rather than as "Prune", because "prune" does not say what will be lost.

Flotilla shows you how many images will go and asks before doing it. Bulk deletion always confirms and that cannot be turned off.

Running a container from an image

Run on an image row opens the run form with that image prefilled. Same form as Run a container…, so there is one screen to learn rather than two.

What the run form gives you

  • Image reference and container name.
  • Published ports, environment variables and volume mounts — each an add-and-remove list with a counter showing how many of the maximum you have used.
  • A Command to run instead of the image's own entrypoint.
  • Detach (run in background) — on for a service, off when you want to watch it.
  • Limit CPU and memory, which reveals the two fields. Left alone, the defaults come from Settings ▸ Resources, and the form says so.
  • A live command preview showing the exact container run … that will execute, updated as you type and validated against the allowlist as you go, with Copy the container command to the clipboard beside it.
  • A verdict rather than a guess: "Valid — ready to run." or "Fix the highlighted field to run this."

Errors stay quiet until a required field has content. An empty image field is not filled in yet, not wrong — an earlier version showed two red validation errors on a form you had not touched.

The preview is the honest part: if the allowlist would refuse what you have typed, you find out on the form rather than after pressing Run.

Empty states say which kind of empty

"Pull one to run a container from it" appears when you have no images. If you have images but a filter excludes them all, it says that instead. An app that draws the same blank pane for both makes you doubt the filter you just set.

Clone this wiki locally