Skip to content

feat(snap): add snap install method to install.sh when available#2250

Open
olivercalder wants to merge 7 commits into
NVIDIA:mainfrom
olivercalder:feature/install-snap-default
Open

feat(snap): add snap install method to install.sh when available#2250
olivercalder wants to merge 7 commits into
NVIDIA:mainfrom
olivercalder:feature/install-snap-default

Conversation

@olivercalder

@olivercalder olivercalder commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Update install.sh to install the OpenShell snap package if snapd is available on the system and the required conditions are met. Also add the OPENSHELL_INSTALL_METHOD environment variable to force a particular install method rather than fall back to automatic system detection.

Related Issue

Based on: #1697 (picking up the work from @zyga)

Addresses: #1674

Changes

  • The install script detects the presence of snapd and if compatible conditions (docker snap) are met, installs the snap from the store.
  • Adds OPENSHELL_INSTALL_METHOD to override automatic detection
    • Supports deb, rpm, snap, and homebrew

Testing

  • mise run pre-commit passes
  • Unit tests added/updated
  • E2E tests added/updated (if applicable)

Checklist

  • Follows Conventional Commits
  • Commits are signed off (DCO)
  • Architecture docs updated (if applicable)

@copy-pr-bot

copy-pr-bot Bot commented Jul 13, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown

All contributors have signed the DCO ✍️ ✅
Posted by the DCO Assistant Lite bot.

@olivercalder

Copy link
Copy Markdown
Contributor Author

I have read the DCO document and I hereby sign the DCO.

@olivercalder

Copy link
Copy Markdown
Contributor Author

recheck

zyga and others added 4 commits July 14, 2026 22:41
…ovisioning

Add snap install path to install.sh so that on Linux systems with snapd
available and no native Docker Engine installed, the installer chooses
the snap path. Snap is preferred when 'snap' command exists and
'snapd.socket' is active.

New functions:
- has_snapd(): detects snapd availability via snap command + snapd.socket
- has_native_docker(): pre-flight check for conflicting native Docker
- install_linux_snap(): installs openshell snap, Docker snap if missing,
  connects interfaces (best-effort), registers gateway via HTTP, verifies status
- register_local_gateway_snap(): gateway add with http:// URL (no mTLS)
- wait_for_local_gateway_listener_snap(): waits for HTTP listener

linux_package_method() returns 'snap' when snapd is available and no native
docker, respecting OPENSHELL_INSTALL_METHOD=classic override.

The installer handles the full flow: installing Docker snap if missing,
connecting all required interfaces, and registering the gateway.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
Fix the snap gateway protocol from https to http to match what the
installer actually registers. Correct misleading claims that the
installer "exits with an error" and "refuses" snap installs on hosts
with native Docker — it silently falls back to classic.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
Adjust the wording of the installation docs for the `openshell` snap. In
particular, clarify when the snap will be installed, how it currently
requires the docker snap, and how to override the installation method.

Also, prune some of the manual interface connections, as these have
since been granted autoconnection via snap store assertions. See:

https://forum.snapcraft.io/t/please-update-snap-declaration-assertion-for-openshell/51762

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
As @drew suggested, add `deb`, `rpm`, `snap`, and `homebrew` as
installation methods which the user can specify via
`OPENSHELL_INSTALL_METHOD` rather than falling back to detecting the
package manager automatically.

`homebrew` is not yet supported on Linux, and it is the only supported
option on MacOS.

Also adds a TODO to implement `install_linux_homebrew` in the future.

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
@olivercalder
olivercalder force-pushed the feature/install-snap-default branch from 70480f2 to 22525a2 Compare July 15, 2026 03:48
@olivercalder

Copy link
Copy Markdown
Contributor Author

Rebased to pull in the changes from #2280 which remove the ssh-keys interface, which was the final remaining interface which required manual connection. Now all interfaces are auto-connected.

@olivercalder

Copy link
Copy Markdown
Contributor Author

We're shipping an improvement to the docker interface in snapd 2.77 to allow the openshell snap to connect to system (non-snap) docker daemons. That will eliminate the requirement for the docker snap to be present. I'm planning to do some testing of snapd latest/edge today to confirm this all works as expected.

Once snapd 2.77 hits latest/stable, the installation instructions and script in this PR can be updated. But we have strict certification processes so it will be several weeks before that happens.

@krishicks

Copy link
Copy Markdown
Collaborator

Would you mind updating the release-canary Github Action to use this? Presently it fetches the snap from the release artifacts and installs it manually, but the other installations (excepting Kubernetes) use install.sh to install the latest available version. In the near future this will all change when we revamp the build and release process, but for now consistency is probably better.

I'm unsure about exposing an env var to force the installation mechanism. To me, the contract of install.sh is to do that for you. The only real knob it has today is which version to install, typically dev. We may be adding additional knobs to the installer later, but I don't think we should incorporate that into this change.

Split the `ubuntu` release canary into three jobs:

- `ubuntu-deb`: test that `install.sh` installs the OpenShell .deb
  package on Ubuntu when snapd is not present.
- `ubuntu-deb-native-docker`: test that `install.sh` installs the
  OpenShell .deb package on Ubuntu when a native docker daemon is
  present -- this will change when snapd 2.77 is released to the
  `latest/stable` channel, which will allow the `openshell` snap to
  connect to non-snap Docker daemons; this job will then be renamed to
  `ubuntu-snap-native-docker`.
- `ubuntu-snap-without-docker`: test that `install.sh` installs both the
  `openshell` and `docker` snaps if snapd is present and there is no
  Docker daemon installed.

In all variants, the canary tests that `openshell status` succeeds after
installation.

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
The change in snapd to support the `openshell` snap connecting to a
non-snap Docker daemon will ship in snapd 2.77, not 2.76.

Also, all the `openshell` snap interfaces are now auto-connected, so
there is no need for manual connection anymore. Remove these from the
description.

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
The contract of `install.sh` is to choose the best installation method
for the host system. Adding the `OPENSHELL_INSTALL_METHOD` variable
breaks that contract, so it is best to remove it.

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
@olivercalder

olivercalder commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

Done and done, thanks for the suggestions.

I've split the ubuntu job into three, as there are three configurations that I think each deserve their own jobs:

  • ubuntu-deb: test that install.sh installs the OpenShell .deb package on Ubuntu when snapd is not present.
  • ubuntu-deb-native-docker: test that install.sh installs the OpenShell .deb package on Ubuntu when a native docker daemon is present.
    • This will change when snapd 2.77 is released to the latest/stable channel, which will allow the openshell snap to connect to non-snap Docker daemons; this job will then be renamed to ubuntu-snap-native-docker.
  • ubuntu-snap-without-docker: test that install.sh installs both the openshell and docker snaps if snapd is present and there is no Docker daemon installed.

In all jobs, test that openshell status works as expected.

The .deb package and snap package both need to be tested, and the snap will need to be tested with both the docker snap and a non-snap Docker daemon, once snapd 2.77 finishes certification testing, so I want to lay the groundwork for that here. The change is already in snapd latest/edge so I want to open another PR based on this PR soon and check that if we tweak the canary job to use snapd latest/edge and install.sh to not gate the snap on docker presence, the openshell snap can now connect to the Docker daemon provided by docker.io in the ubuntu-latest runner.

@krishicks krishicks added the test:e2e Requires end-to-end coverage label Jul 17, 2026
@github-actions

Copy link
Copy Markdown

Label test:e2e applied, but pull-request/2250 is at {"messa while the PR head is c91aea9. A maintainer needs to comment /ok to test c91aea97067e56503eb746e8601e26dfe95f61bb to refresh the mirror. Once the mirror catches up, re-run Branch E2E Checks from the Actions tab.

fi
sudo systemctl start docker || sudo service docker start
mkdir -p "${HOME}/.config/openshell"
printf 'OPENSHELL_DRIVERS=docker\n' > "${HOME}/.config/openshell/gateway.env"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this line required? I would have expected gateway auto-detection of Docker to work.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not certain, it may work without. This is directly copied from the existing ubuntu job (now called ubuntu-deb-native-docker). These two jobs are largely equivalent at the moment, but ubuntu-deb-native-docker will become ubuntu-snap-native-docker once native docker support works by default.

When installing from the Snap Store, snapd automatically connects the `home`,
`network`, and `network-bind` plugs. The `docker` plug still
requires manual connection:
The OpenShell snap requires the Docker snap, which you can install with:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the OpenShell snap requires the Docker snap, does that also mean it cannot be used with Podman? I'm wondering what this means for auto-detection, which prefers Podman over Docker. It seems like we need to document this limitation of the snap more clearly, if that's the case. I'd expect to see the docs say very clearly that the snap only works with Docker and you should configure Docker as the openshell driver explicitly to avoid issues if you also have Podman running.

@olivercalder olivercalder Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, yes, the OpenShell snap requires the Docker snap. But we can fairly easily extend this:

Non-snap Docker

We landed support for connecting to a non-snap Docker daemon in snapd 2.77, being cut nowish and released to latest/stable in ~6 weeks. But we can test this immediately by trying snapd latest/edge channel in the install scripts/jobs.

Podman

We can support connecting to Podman by requesting a store assertion to allow connecting the podman interface on https://forum.snapcraft.io/. We at Canonical can of course drive this request, but before doing so we should probably settle on a direction regarding running the gateway daemon as a system or user service.

As for why we need a store assertion for connecting to Podman but not to Docker: there's a docker snap which provides the docker-daemon slot for OpenShell's docker plug to connect to (and the new docker interface change allows connecting to the system slot, i.e. non-snap Docker daemon), but there is no snap of Podman. The current podman interface disallows connection without an assertion, which is a bit weird, usually super-privileged interfaces just deny installation without an assertion, but for historical reasons the docker and podman interfaces were defined a bit differently from most interfaces.

System vs user service

The most important thing to decide IMO is whether we want to run the OpenShell gateway daemon as a system service or user service. Currently, it's a system service, which has WantedBy=multi-user.target, so it starts as root after boot. This makes sense with Docker, as Docker also runs as root as a system service. AFAIK you can run podman as a system service, but a major benefit of Podman is that it can run rootless as a user service. It doesn't make sense for an OpenShell gateway system daemon to try to talk to a podman user service, so it feels like if we want to prioritize rootless Podman support, we probably want to run the gateway as a user service.

This change could be made immediately, if desired. This can be done by adding daemon-scope: user to snapcraft.yaml next to the daemon: simple field. The user-daemons feature in snapd is still experimental, but there's an override in snapd which already allows particular snaps to run user daemons, and openshell is one of the snaps explicitly allowed -- that means users/install.sh would not need to manually enable the user-daemons feature. Additionally, the snapd team (and I in particular) are working to polish that feature and enable it by default for the next snapd release or so (2.78 or 2.79).

But, running as a user service has potential drawbacks IMO, which are not specific to the snap or any packaging format. Currently, you can install the openshell snap in a headless environment and connect to it, since it runs as root, no user logged in. But if the gateway service runs as a user daemon, then you'd need to log in with a particular user in order to start the gateway, and then enable linger or run in tmux or something if you want that service to persist. And since (AFAIK) the gateway always tries to listen on the same port, then if more than one user logs into a system, the gateway will fail to start for everyone but the first user who happened to log in.

My understanding is there is discussion about switching to using a unix domain socket for the gateway user service instead of listening on a port, and that would indeed fix the user conflict issue, though if there's only a user service then there still wouldn't be a good way to connect to connect to the gateway if it's not running on your local machine, nor to handle running in headless boxes without user logged in.

So it feels to me like there are some situations where one would reasonably want to run as a system service (e.g. in headless environment) and some where a user service makes more sense (e.g. on a local end-user machine). I see a few possibilities:

  1. Just include two daemons in the snapcraft.yaml apps stanza, one of which is a system daemon and one of which is a user daemon, and run both
    • This would require the user daemon to not try to bind to the same port as the system daemon, though, else they'd conflict and fail to start one or the other service
  2. Add the ability to select at install time whether you want to include the system service or user service, or neither
    • This sounds like a good use for snap components
    • You'd end up with something like snap install openshell+system-gateway or snap install openshell+user-gateway
    • Then snap install openshell should do some sensible default, probably install the user gateway component, as running the gateway as a user service is presumably less privileged, and more what an end user running install.sh or snap install openshell is likely to want
  3. Add the ability to configure this after installation
    • This would likely be done via snap configuration options which we can define to enable or disable the system or user service OpenShell gateway depending on the setting.
    • You'd end up with something like snap set openshell gateway-scope=system or snap set openshell gateway-scope=user, with the default probably being user

Personally, I feel like it's a bad idea to switch the gateway to run as a user service without first switching to using unix domain sockets instead of listening on a fixed port. But this is an upstream OpenShell concern, not specific to the snap packaging. And we're happy to help package whatever configuration you'd prefer.

@krishicks

Copy link
Copy Markdown
Collaborator

/ok to test c91aea9

@krishicks krishicks removed the test:e2e Requires end-to-end coverage label Jul 17, 2026
@krishicks

krishicks commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

I ran this with e2e but what I actually meant to do was run the release-canary workflow for it, which I did here: https://github.com/NVIDIA/OpenShell/actions/runs/29593468971

The snap installation without Docker failed, which we expect until this is merged and a new snap is published. The others succeeded, though, which is good.

@krishicks krishicks self-assigned this Jul 17, 2026

@olivercalder olivercalder left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review. I've written up some notes about the current state of things regarding the openshell snap connecting to snap/non-snap Docker, Podman, and running as a system vs user service.

When installing from the Snap Store, snapd automatically connects the `home`,
`network`, and `network-bind` plugs. The `docker` plug still
requires manual connection:
The OpenShell snap requires the Docker snap, which you can install with:

@olivercalder olivercalder Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, yes, the OpenShell snap requires the Docker snap. But we can fairly easily extend this:

Non-snap Docker

We landed support for connecting to a non-snap Docker daemon in snapd 2.77, being cut nowish and released to latest/stable in ~6 weeks. But we can test this immediately by trying snapd latest/edge channel in the install scripts/jobs.

Podman

We can support connecting to Podman by requesting a store assertion to allow connecting the podman interface on https://forum.snapcraft.io/. We at Canonical can of course drive this request, but before doing so we should probably settle on a direction regarding running the gateway daemon as a system or user service.

As for why we need a store assertion for connecting to Podman but not to Docker: there's a docker snap which provides the docker-daemon slot for OpenShell's docker plug to connect to (and the new docker interface change allows connecting to the system slot, i.e. non-snap Docker daemon), but there is no snap of Podman. The current podman interface disallows connection without an assertion, which is a bit weird, usually super-privileged interfaces just deny installation without an assertion, but for historical reasons the docker and podman interfaces were defined a bit differently from most interfaces.

System vs user service

The most important thing to decide IMO is whether we want to run the OpenShell gateway daemon as a system service or user service. Currently, it's a system service, which has WantedBy=multi-user.target, so it starts as root after boot. This makes sense with Docker, as Docker also runs as root as a system service. AFAIK you can run podman as a system service, but a major benefit of Podman is that it can run rootless as a user service. It doesn't make sense for an OpenShell gateway system daemon to try to talk to a podman user service, so it feels like if we want to prioritize rootless Podman support, we probably want to run the gateway as a user service.

This change could be made immediately, if desired. This can be done by adding daemon-scope: user to snapcraft.yaml next to the daemon: simple field. The user-daemons feature in snapd is still experimental, but there's an override in snapd which already allows particular snaps to run user daemons, and openshell is one of the snaps explicitly allowed -- that means users/install.sh would not need to manually enable the user-daemons feature. Additionally, the snapd team (and I in particular) are working to polish that feature and enable it by default for the next snapd release or so (2.78 or 2.79).

But, running as a user service has potential drawbacks IMO, which are not specific to the snap or any packaging format. Currently, you can install the openshell snap in a headless environment and connect to it, since it runs as root, no user logged in. But if the gateway service runs as a user daemon, then you'd need to log in with a particular user in order to start the gateway, and then enable linger or run in tmux or something if you want that service to persist. And since (AFAIK) the gateway always tries to listen on the same port, then if more than one user logs into a system, the gateway will fail to start for everyone but the first user who happened to log in.

My understanding is there is discussion about switching to using a unix domain socket for the gateway user service instead of listening on a port, and that would indeed fix the user conflict issue, though if there's only a user service then there still wouldn't be a good way to connect to connect to the gateway if it's not running on your local machine, nor to handle running in headless boxes without user logged in.

So it feels to me like there are some situations where one would reasonably want to run as a system service (e.g. in headless environment) and some where a user service makes more sense (e.g. on a local end-user machine). I see a few possibilities:

  1. Just include two daemons in the snapcraft.yaml apps stanza, one of which is a system daemon and one of which is a user daemon, and run both
    • This would require the user daemon to not try to bind to the same port as the system daemon, though, else they'd conflict and fail to start one or the other service
  2. Add the ability to select at install time whether you want to include the system service or user service, or neither
    • This sounds like a good use for snap components
    • You'd end up with something like snap install openshell+system-gateway or snap install openshell+user-gateway
    • Then snap install openshell should do some sensible default, probably install the user gateway component, as running the gateway as a user service is presumably less privileged, and more what an end user running install.sh or snap install openshell is likely to want
  3. Add the ability to configure this after installation
    • This would likely be done via snap configuration options which we can define to enable or disable the system or user service OpenShell gateway depending on the setting.
    • You'd end up with something like snap set openshell gateway-scope=system or snap set openshell gateway-scope=user, with the default probably being user

Personally, I feel like it's a bad idea to switch the gateway to run as a user service without first switching to using unix domain sockets instead of listening on a fixed port. But this is an upstream OpenShell concern, not specific to the snap packaging. And we're happy to help package whatever configuration you'd prefer.

fi
sudo systemctl start docker || sudo service docker start
mkdir -p "${HOME}/.config/openshell"
printf 'OPENSHELL_DRIVERS=docker\n' > "${HOME}/.config/openshell/gateway.env"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not certain, it may work without. This is directly copied from the existing ubuntu job (now called ubuntu-deb-native-docker). These two jobs are largely equivalent at the moment, but ubuntu-deb-native-docker will become ubuntu-snap-native-docker once native docker support works by default.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants