Skip to content

Releases: amslertec/containly

Containly 2.7.4

Choose a tag to compare

@amslertec amslertec released this 23 Oct 21:25

Fixed

  • Backups failed with "No such image: alpine:latest" when the helper image was removed from the
    host.
    Containly checked its backup helper image only once per process: after an image prune or
    a host cleanup removed it later, the image was never pulled again and every backup (scheduled
    and manual) failed with (HTTP code 404) no such container - No such image: alpine:latest until
    Containly was restarted. The helper image is now checked before every use and pulled again when
    it is missing; if it disappears between the check and the start of the helper, Containly pulls
    it once more and retries. Concurrent backups still share one pull.
  • Helper image pinned. Backup, restore, folder browser and volume file helpers no longer use
    the moving alpine:latest tag but a pinned Alpine 3.22.2 image by its multi-arch digest (the
    same image the NFS target already used), so every host runs the same known tools. The helpers
    keep their ch.amslertec.containly.helper label. The Docker CLI helper is re-checked and
    re-pulled the same way.

Changed

  • Empty states centred. Empty states (for example Updates > "All up to date", empty lists,
    no search hits, empty backup sections, settings tabs) now sit centred as one block, icon, title,
    text and button, horizontally and vertically in the free content area instead of top-left, the
    same on every page, on desktop and phone, in light and dark mode.

Update: docker compose pull && docker compose up -d (or pull amslertec/containly:2.7.4).

Full changelog: v2.7.3...v2.7.4

Containly 2.7.3

Choose a tag to compare

@amslertec amslertec released this 20 Oct 05:41

Fixed

  • Compose override files are honoured everywhere. Containly called `docker compose -f `, and an explicit `-f` switches off Docker's automatic loading of `compose.override.yaml`. Services or settings defined only in an override file (for example a Mailpit service with its port mapping) were ignored by every deploy, pull, start, stop, restart, down and logs action from Containly, and a version update of such an image failed with "version tag not found". Containly now passes every compose file of a stack: the files Docker recorded in the `com.docker.compose.project.config_files` label of the stack's containers (in that order, plus a default override file that exists but is missing there, for example after an older Containly redeployed the stack with the main file only), or, before the first deploy, the main file plus `compose.override.yaml` / `.yml` / `docker-compose.override.yaml` / `.yml` exactly like `docker compose` does without `-f`. Only regular files inside the stack folder are used (no paths outside it, no symlinks). This applies to the stack actions, the app catalog, Git stacks, backups and restores, and stack migration.
  • Version updates across compose files. Updating a pinned version tag now searches every
    compose file of the stack and .env, and patches exactly the file(s) that define the tag (inline
    or through a variable). When several files define it, all are updated together; if the deploy
    fails, every changed file is restored. When nothing matches, the message names the files that
    were searched.

Added

  • Override files in the stack editor. When a stack has override files, the editor shows them as
    tabs next to the main file (for example compose.yaml and compose.override.yaml), with the same
    checks, and the "Check before deploy" diff now includes saved changes in override files. A deploy
    from the live terminal now also becomes the baseline for that diff.
  • API tokens (Free). Profile, API tokens creates personal access tokens with a name, scopes and
    an optional expiry (30 days, 90 days, 1 year or none): Read (lists of hosts, containers,
    stacks, images, updates, backups), Redeploy stacks (optionally limited to picked stacks) and
    Prometheus metrics. The token is shown once; Containly stores only an HMAC of it. The list
    shows the last use and IP; tokens can be revoked at any time, and admins see and revoke every
    user's tokens under Settings, Integrations. Tokens are accepted only as Authorization: Bearer
    on an allow-list of API routes, never from a cookie, never for WebSockets, consoles, users,
    settings or token management. A token never does more than its owner's current role: a viewer
    cannot create a redeploy token, and tokens of a demoted, disabled or deleted user stop working.
    Every action shows in the audit log as <user> (Token: <name>). No CSRF header is needed with
    a bearer token; requests are rate limited per token.
  • CI redeploy API (Free). POST /api/stacks/<id>/redeploy or POST /api/stacks/redeploy with
    {"name": "<stack>", "endpoint": "<host id, optional>"} and a token with the Redeploy scope runs
    git pull for Git stacks, docker compose pull and up -d with all compose files, and answers
    202 with a job; GET /api/jobs/<id> reports queued, running, ok or error with the last
    output. A second request while a redeploy of the same stack runs returns the running job. At most
    10 redeploys per minute per token.
  • Prometheus export (Free). Settings, Integrations, Prometheus switches on GET /metrics
    (text exposition format, off by default): endpoint up, host CPU, memory and disk, containers per
    state, and per container state, health, restart count, CPU and memory, labelled with endpoint,
    stack, container and image (no container IDs, image digests removed, at most 500 containers per
    endpoint). The values come from the samples Containly already takes every minute, so a scrape
    never calls Docker. A token with the Prometheus metrics scope is required unless the admin allows
    open scrapes for a trusted network. The card shows a ready prometheus.yml scrape config.
    Inbound only: Containly sends nothing anywhere.
  • Demo: sample API tokens on the token pages (creating is disabled with a hint), the
    Prometheus export shown switched off.

Security

  • Token requests skip CSRF only because a bearer header cannot be sent cross-site without CORS,
    which Containly never allows; a bearer header always wins over a session cookie, so the two are
    never mixed. An invalid bearer token is refused also when open scrapes are allowed.
  • Migrations 48 (stack_deploys.extra_files) and 49 (api_tokens).

Update: docker compose pull && docker compose up -d (image amslertec/containly:2.7.3 or latest).

Full changelog: v2.7.2...v2.7.3

Containly 2.7.2

Choose a tag to compare

@amslertec amslertec released this 19 Oct 11:58

Added

  • Four more notification channels (Free). Settings, Notifications, Channels now also offers:
    • Discord: paste the channel's webhook URL; messages arrive as an embed coloured by
      severity (blue info, amber warning, red critical) with time, affected object and a link back
      to Containly. Mentions are switched off and Markdown in names is escaped.
    • Telegram: your own bot token (from @Botfather), a chat ID (number, -100... for groups
      and channels, or @channelname) and an optional topic ID for groups with topics. HTML
      formatting with escaped text and a link button text in the channel's language.
    • Slack: an incoming webhook URL; messages use blocks (header, text, context, an "Open in
      Containly" button) inside an attachment coloured by severity, plus a plain fallback text.
    • Pushover: app token and user (or group) key, an optional device, and a priority per
      severity from -2 to 2. Defaults are info -1, warning 0, critical 1; emergency priority 2 is
      never a default, and choosing it shows a warning plus the repeat interval and stop time.
      All four have the existing per-channel filter (minimum severity, events, message language),
      "Send test" that saves nothing, delivery log, rate limit and retries.
  • Web push to your own devices (Free). Profile, Push notifications turns on push for the
    current browser or the installed app (PWA) and lists every device of your account with rename,
    minimum severity and event filter per device, "Send test" and remove. You get the
    notifications for which you are a recipient under Settings, Notifications, also without an
    e-mail address. Clicking a notification opens the relevant page in Containly. The page explains
    why push is not available when the browser does not support it, the page is not served over
    HTTPS, notifications are blocked, or an iPhone or iPad has not added Containly to the home
    screen (Share, Add to Home Screen, iOS 16.4 or later).
  • Workflows (Pro): the notify step can also send push to devices (the recipients'
    devices that pass their own minimum severity), next to in-app, e-mail and saved channels.
    Checked on the server: on Free a workflow push is refused.

Security

  • Discord and Slack webhook URLs are only accepted on the official hosts and paths
    (https://discord.com/api/webhooks/..., also discordapp.com, ptb. and canary.;
    https://hooks.slack.com/...); Telegram and Pushover requests only ever go to
    api.telegram.org and api.pushover.net. The URLs, bot token, app token and user key are
    stored encrypted like all channel secrets and are never returned; the list shows a masked hint
    (host and the last four characters of the webhook ID).
  • Web push needs no third-party service: Containly generates its own VAPID key pair once (private
    key stored encrypted), encrypts every message for the receiving browser (RFC 8291, aes128gcm)
    and posts it only to the browser vendors' push services (Google FCM, Mozilla, Apple, Microsoft
    WNS). The push service only sees ciphertext. Subscription endpoints and keys are stored
    encrypted and never returned by the API. A subscription is deleted as soon as the push service
    answers 404 or 410 (app removed, permission revoked), and with its user.
  • Per device at most 20 push messages per minute; temporary errors are retried with the same
    backoff as the channels.

Changed

  • Database migration 47 adds the table push_subscriptions; the VAPID key pair is stored in the
    instance settings (webpush_vapid).
  • The channel type in the add dialog is now a dropdown (seven types).
  • The demo shows sample Discord, Telegram, Slack and Pushover channels (test simulated, nothing is
    sent); the push page is visible but disabled with a hint, because push needs your own instance
    with HTTPS.

Notes

  • The Discord, Telegram, Slack and Pushover request formats are verified against mock receivers
    that check the vendor request shapes; no real vendor accounts were used before this release.
    Web push was verified end to end with Google Chrome and the real FCM push service.

Containly 2.7.1

Choose a tag to compare

@amslertec amslertec released this 19 Oct 09:16

Added

  • Notification channels (Free). Settings, Notifications has a new Channels card: send the
    existing notifications to ntfy (ntfy.sh or your own server; access token or user and
    password, priority per severity, optional click URL), Gotify (app token, priority per
    severity) or a generic webhook (POST with a stable JSON body, optional HMAC-SHA256
    signature, up to 10 custom headers). Every notification type that is switched on under Events
    can go to channels; each channel has its own minimum severity, an optional subset of events
    and a message language.
  • Send test in the add and edit dialog sends one real message with the entered settings and
    saves nothing; the result is translated (for example "Sign-in refused (HTTP 401)" or "loopback
    addresses are never allowed") and disappears as soon as a field changes. Saved channels have a
    "Send test" button in the list.
  • Delivery log per channel (last 20 attempts: time, event, result, HTTP status, attempts;
    never message content or secrets) and a status per channel (working, error, failing after 10
    failed deliveries in a row).
  • Workflows (Pro): the notify step can pick saved channels next to in-app and e-mail. Using
    channels in workflows needs Pro, checked on the server; normal notifications to channels stay
    Free.
  • Generic webhook format (version 1): {version, id, type, severity, title, message, target, link, at, test, source} with the headers X-Containly-Event, X-Containly-Delivery and, when signed,
    X-Containly-Timestamp plus X-Containly-Signature: sha256=<hex HMAC-SHA256 of "<timestamp>.<body>">. Rate limit summaries use the type channel.suppressed with a
    suppressed count.

Security

  • Channel secrets (tokens, passwords, signing secret, header values) are stored AES-256-GCM
    encrypted with the instance master key and are never returned by the API, written to logs or
    to the audit log. Changing the destination (scheme, host or port) of a saved channel requires
    entering its secrets again, so a stored token can never be forwarded to another server, also
    not by a draft test.
  • Outbound policy for channel URLs (SSRF): loopback, localhost, link-local, cloud metadata
    addresses (169.254.169.254, fd00:ec2::254, 100.100.100.200), reserved ranges and Containly's
    own address and port are always refused; every resolved address is checked and the connection
    is pinned to it (no DNS rebinding); no redirects are followed; plain http:// is only allowed
    to private network addresses. Private and LAN addresses are allowed by default because ntfy and
    Gotify usually run there; the admin switch "Block private targets" (or
    CONTAINLY_CHANNELS_BLOCK_PRIVATE=1, which forces it on) refuses them too.
  • Per channel at most 20 messages per minute; anything above is held back and summarised in one
    message. Transient errors (HTTP 429, 5xx, timeouts, connection errors) are retried 3 times with
    backoff (1 s, 2 s, 4 s, Retry-After honoured up to 30 s); other errors are not retried.

Changed

  • Database migration 46 adds the tables notification_channels and notification_deliveries.
  • The demo shows two sample channels with a delivery log; its test button is simulated and it
    never sends anything.

Containly 2.7.0

Choose a tag to compare

@amslertec amslertec released this 17 Oct 19:45

Added

  • Workflows (Pro). A new Workflows page automates routine reactions as typed rules:
    a trigger, optional conditions (all or any) and ordered steps, with an "otherwise" branch.
    No scripting and no free-form commands: triggers, conditions and steps are a fixed list.
    • Triggers: container unhealthy, container exited unexpectedly or killed for lack of memory
      (OOM), restart loop, host offline (for a set time) and back online, sustained CPU or memory
      above a threshold, backup finished (failed, succeeded or any result) and a schedule with the
      app's own time picker (hourly, chosen weekdays, monthly).
    • Conditions: CPU or memory threshold, duration of the state, data freshness, host
      reachable, backup target reachable and "not held by a running backup or restore". An unknown
      value counts as not met, so nothing changes on stale data.
    • Steps: notify (in-app and/or e-mail, recipients under Settings, Notifications, Workflow
      notifications), restart, start or stop a container, run a backup plan and wait for its
      result, wait until healthy, update a container, and wait.
    • Backup before update: an update step only runs after a successful backup step earlier in
      the same run. A failed backup, or one that finished with warnings, blocks the update, and the
      run history says why. The update pulls the newest image for the container's tag and recreates
      only that container.
    • Safety: changing steps act only on containers or stacks you pick explicitly, never on a
      whole host. Each event is handled once per workflow, host and resource (the same container
      name on another host is separate); per-resource cooldown; a circuit breaker (default three
      changing actions per 60 minutes) pauses the workflow for that resource until an admin re-arms
      it; a workflow's own actions never trigger another workflow; locked or offline hosts are
      skipped; at most three runs at a time. Every action is written to the audit log as
      "Workflow: ". Engine state (cooldowns, breakers, attempts, runs) survives restarts.
    • Templates and builder: 20 ready templates in six groups (Health and recovery, Backups,
      Updates, Hosts, CPU and memory, Schedules and maintenance), for example unhealthy or crashed
      container: notify and restart; restart loop alert; backup failed or succeeded; nightly or
      weekly backup only when host and target are ready; weekly or monthly backup, update and health
      check; host offline or back online; sustained CPU or memory alerts and restarts; weekly
      restart, stop every night and start every morning. Every group starts closed and only one
      group is open at a time (opening another closes the previous one), with mouse or keyboard.
      Your own workflows are listed under their own "My workflows" heading with a count. The
      builder shows the flow from trigger to steps, with an inline editor for each part. New
      workflows start as drafts and do nothing until you pick their containers, hosts or plans and
      enable them.
    • Simulate and history: a dry run checks the conditions against the live state and shows
      which steps would run, without changing anything or sending notifications. "Run now" starts
      the real steps once with every safety check. The history shows each run with its condition
      results and step trace.
    • Only admins create, edit, enable or run workflows; viewers can read them. On Free the page
      shows the templates as a read-only preview with an upgrade hint, and existing workflows pause
      with reason "licence" until Pro is active again (they resume on their own). This is enforced
      on the server, not only in the interface.
    • Available in English, German and Spanish.
  • Demo: the public demo shows four sample workflows (two enabled, two drafts) with a short
    run history, including a tripped circuit breaker. Simulate works there; "Run now" is refused
    and the engine never acts on the simulated hosts.

Fixed

  • German interface: two messages still written with "ß" (password rule "at least one uppercase
    letter" and the migration warning about bind mounts outside the allowed paths) now use the
    Swiss spelling ("ss").

Update: docker compose pull && docker compose up -d (image amslertec/containly:2.7.0), or the one-click self-update in the app.

Full changelog: v2.6.2...v2.7.0

Containly 2.6.2

Choose a tag to compare

@amslertec amslertec released this 16 Oct 09:57

Security

  • Base image moved to Alpine 3.23. Both build stages of the app and demo images now use
    node:22-alpine3.23 pinned by digest (Node.js 22.23.3), with current OS package fixes applied
    at build time.
  • Samba client 4.22.10 fixes CVE-2026-2340. The SMB client used for SMB backup targets moves
    from 4.21.9 to 4.22.10-r0, the Alpine release that fixes CVE-2026-2340. SMB backups keep running
    through the same confined, encrypted SMB3 client; it was re-tested against a real Samba server.
  • OpenLDAP libraries 2.6.13 (were 2.6.8), pulled in by the Samba client. Some scanners report
    CVE-2026-22185 against this package; that issue is in the LMDB mdb_load tool, which the
    OpenLDAP package does not build and Containly never runs.
  • Known findings without an upstream fix: CVE-2025-60876 (BusyBox wget) and CVE-2025-62813
    (lz4) have no fixed package in Alpine yet. Containly does not call wget, and lz4 is only a
    library dependency of the Samba client with no fixed release available; both will be picked up
    automatically by the next image build once Alpine ships a fix.

Full changelog: v2.6.1...v2.6.2

Containly 2.6.1

Choose a tag to compare

@amslertec amslertec released this 15 Oct 22:16

Changed

  • Grouped lists start closed. When a list is grouped (by stack, host, image, driver or
    resource type), every group starts collapsed and shows only its header with the counts; a click
    opens it. "Per page" now counts groups, so a page is a compact overview. Select all and keyboard
    navigation only reach rows of opened groups.
  • Images grouped by image across hosts. In the Images view each repository is one group even
    under All hosts (for example alpine on five hosts is one group, the header says how many hosts),
    and every row names its host. The images themselves stay separate per host.
  • Toolbar dropdowns share one row on phones. View, Sort and Per page sit side by side whenever
    they fit, instead of wrapping onto a second row. On phones the View dropdown shows its icon and
    value without the word "View", so the row also fits in German and Spanish.

Fixed

  • The dashboard on phones could scroll sideways: a long event detail without spaces (for example
    an image reference with a digest) widened the page. Long names and references on the dashboard
    now wrap inside their cards.

Containly 2.6.0

Choose a tag to compare

@amslertec amslertec released this 15 Oct 21:38

Added

  • View and grouping selector on every resource list. Containers can be shown as a list, by
    Compose stack (plus a group for standalone containers) or, under All hosts, by host. Images can
    be shown as a list, as Images (all tags and versions of a repository together; different
    registries stay separate), by the stacks that use them or by host. Volumes and networks group by
    stack (from their real Compose labels and the containers that use them, with Standalone and
    Unused groups) or by host; networks keep the driver grouping. The backup overview groups by
    stack, resource type or host. A resource shared by several stacks appears under each of them,
    while selection and bulk actions still count it once (per host and ID); identical stack names on
    different hosts never merge. The selector is one accessible dropdown, also on phones, the choice
    is remembered per page, and switching from All hosts to a single host falls back from Host to
    List.
  • Entra ID users show their sign-in name. The user list, user detail and profile show the
    Entra preferred_username (UPN) instead of the object ID. The immutable object ID stays the
    only key that links a sign-in to an account and is shown separately as "Entra object ID" in the
    account details. Existing Entra users get their sign-in name on their next sign-in. Local and
    Entra accounts are never merged by e-mail or UPN.

Changed

  • User detail. Reset password is only in the three-dot menu (no separate header button on
    desktop or phone), the role and sign-in source badges sit side by side in one row, and the
    username is shown once (below the display name only when it differs).
  • Container logs in English. All operator-facing Docker logs (startup, migrations, background
    jobs, helpers, licence checks and the first-setup banner with the setup token) are English,
    independent of the UI language. The UI and API messages stay translated.
  • Dark mode multi-select bar. The bulk action bar is a raised dark surface with a border in
    dark mode instead of a bright white bar (containers, images, volumes, networks, stacks, hosts,
    updates, users). Light mode is unchanged.
  • The support address is now support@containly.ch (README, Docker Hub page, security policy, the
    licence file and the licence error messages).

Fixed

  • Restart-loop notification never fired. The background monitor read the restart count from
    the container list, which does not carry it, so every container counted as 0 restarts. The
    count now comes from cached inspect data (refreshed only for running or restarting containers
    that may have restarted), and the "restart loop" notification is sent again.
  • Phones: Most restarts card. Long container and host names no longer push the restart count
    outside the card; the host sits under the name and long names wrap.
  • Phones: Compose editor. In the stack editor and the new stack dialog the editor frame stays
    inside the screen; swiping sideways scrolls only the text, not the toolbar, dialog or page.

v2.5.2

Choose a tag to compare

@amslertec amslertec released this 14 Oct 22:25

Changed

  • Demo start page in German, English and Spanish. The demo gateway now speaks the visitor's
    language on every page it renders: start page, session ended, expired, maintenance, capacity
    and rate-limit notices. A flag menu (Germany, United Kingdom, Spain) switches the language
    with mouse or keyboard. The language is picked from an explicit ?lang= link parameter
    (the website passes its current language), then the stored demo choice, then the browser
    language, then English. The chosen language is carried into the demo session, so the app
    opens in it, and an explicit language change inside the demo is kept across logout, restart
    and expiry.

Fixed

  • The demo start page was always German for visitors whose browser or website language was not
    German, because the website language never reached the demo host.

v2.5.1

Choose a tag to compare

@amslertec amslertec released this 14 Oct 10:19

Added

  • Test connection in backup target dialogs. New and edit dialogs for all six target types
    (local folder, host folder, S3, SFTP, SMB, NFS) have an optional "Test connection" button that
    checks the unsaved settings before you save. The test writes a uniquely named object, reads it
    back, compares it and removes it again; it never creates folders, never overwrites existing
    files, and reports a failed clean-up instead of claiming success. Nothing is saved, pinned or
    recorded by a test. A stored secret is only reused for the same target and destination; if you
    change the destination, enter the secret again. The result is cleared when a field changes. In
    the demo the test is simulated without network access.
  • "With backups" filter. The backup overview has a new segment after "All" that lists only
    hosts and resources that have at least one complete backup set (English, German, Spanish). The
    counts follow the selected host and search.

Changed

  • The connection test of saved targets uses the same safer probe (exclusive test object, clean-up
    reporting, translated error messages).

Fixed

  • Backup list: icons, names and the metadata line no longer touch the row borders; rows keep
    even vertical spacing and centred actions.