I have sinned, dear Father // Father, I have sinned
1# Running Snaps Without Systemd: A Manual Approach
2
3## Overview
4
5This guide documents how to manually run a simple snap (command-line tool, no services) when systemd is not available. This is an educational exercise to understand snap internals—**this approach is NOT recommended for production use**.
6
7## Prerequisites
8
9- A `.snap` file (the snap package you want to run)
10- `unsquashfs` or `squashfs-tools` installed
11- Basic understanding of Linux filesystems and environment variables
12- Root or appropriate permissions for mounting
13
14## Understanding Snap Structure
15
16A `.snap` file is a **SquashFS filesystem** containing:
17- `/meta/snap.yaml` - Snap metadata
18- Application binaries and data
19- Libraries and dependencies
20- Desktop files, icons, etc.
21
22The snap expects to be mounted at a specific location (default: `/snap/<snap-name>/<revision>`).
23
24## Manual Steps to Run a Snap
25
26### Step 1: Extract or Mount the Snap
27
28#### Option A: Extract the Snap (Simpler, No Root Required)
29
30```bash
31# Extract the snap to a directory
32mkdir -p ~/snaps/extracted
33unsquashfs -d ~/snaps/extracted/my-snap /path/to/my-snap_1.0_amd64.snap
34
35# Note the extraction path
36SNAP_MOUNT=/home/yourusername/snaps/extracted/my-snap
37```
38
39#### Option B: Mount the Snap (More Authentic)
40
41```bash
42# Create a mount point
43sudo mkdir -p /snap/my-snap/x1
44
45# Mount the squashfs file
46sudo mount -t squashfs -o ro,nodev /path/to/my-snap_1.0_amd64.snap /snap/my-snap/x1
47
48# Set mount point
49SNAP_MOUNT=/snap/my-snap/x1
50```
51
52### Step 2: Parse the Snap Metadata
53
54```bash
55# Read the snap.yaml to find the app command
56cat $SNAP_MOUNT/meta/snap.yaml
57
58# Example output:
59# name: hello
60# version: 2.10
61# apps:
62# hello:
63# command: bin/hello
64```
65
66### Step 3: Set Up Required Environment Variables
67
68Snaps expect specific environment variables to be set. Create a script or export these:
69
70```bash
71#!/bin/bash
72
73# Basic snap environment variables
74export SNAP="$SNAP_MOUNT"
75export SNAP_NAME="my-snap"
76export SNAP_VERSION="1.0"
77export SNAP_REVISION="x1"
78export SNAP_ARCH="amd64" # or your architecture
79
80# Data directories (writable areas for the snap)
81export SNAP_DATA="/var/snap/$SNAP_NAME/$SNAP_REVISION"
82export SNAP_COMMON="/var/snap/$SNAP_NAME/common"
83
84# User-specific data directories
85export SNAP_USER_DATA="$HOME/snap/$SNAP_NAME/$SNAP_REVISION"
86export SNAP_USER_COMMON="$HOME/snap/$SNAP_NAME/common"
87
88# Instance-related (for regular snaps, same as SNAP_NAME)
89export SNAP_INSTANCE_NAME="$SNAP_NAME"
90export SNAP_INSTANCE_KEY=""
91
92# Library path for graphics drivers, etc.
93export SNAP_LIBRARY_PATH="/var/lib/snapd/lib/gl:/var/lib/snapd/lib/gl32"
94
95# User ID information
96export SNAP_UID=$(id -u)
97export SNAP_EUID=$(id -u)
98
99# Exposed home directory (uppercase version)
100export SNAP_REAL_HOME="$HOME"
101
102# For classic confinement, preserve TMPDIR
103# For strict confinement, you might want a snap-specific tmp
104# export TMPDIR="/tmp/snap.$SNAP_NAME"
105```
106
107### Step 4: Create Data Directories
108
109```bash
110# Create system-wide data directories (may require sudo)
111sudo mkdir -p /var/snap/$SNAP_NAME/$SNAP_REVISION
112sudo mkdir -p /var/snap/$SNAP_NAME/common
113
114# Create user data directories
115mkdir -p $HOME/snap/$SNAP_NAME/$SNAP_REVISION
116mkdir -p $HOME/snap/$SNAP_NAME/common
117```
118
119### Step 5: Run the Application
120
121```bash
122# Source the environment (or use the script above)
123source /path/to/snap-env.sh
124
125# Execute the command from snap.yaml
126# If the command is "bin/hello", then:
127$SNAP/bin/hello [arguments]
128
129# Or more generally:
130cd $SNAP
131./path/to/command
132```
133
134### Complete Example Script
135
136Here's a complete script to run a simple snap:
137
138```bash
139#!/bin/bash
140set -e
141
142# Configuration
143SNAP_FILE="/path/to/my-snap_1.0_amd64.snap"
144SNAP_NAME="my-snap"
145SNAP_REVISION="x1"
146EXTRACT_DIR="$HOME/snaps/extracted"
147MOUNT_DIR="$EXTRACT_DIR/$SNAP_NAME"
148
149# Step 1: Extract the snap
150echo "Extracting snap..."
151mkdir -p "$EXTRACT_DIR"
152unsquashfs -f -d "$MOUNT_DIR" "$SNAP_FILE"
153
154# Step 2: Parse snap.yaml to find the command
155echo "Reading snap metadata..."
156COMMAND=$(grep -A 2 "^ $SNAP_NAME:" "$MOUNT_DIR/meta/snap.yaml" | grep "command:" | awk '{print $2}')
157VERSION=$(grep "^version:" "$MOUNT_DIR/meta/snap.yaml" | awk '{print $2}')
158
159echo "Found command: $COMMAND"
160echo "Version: $VERSION"
161
162# Step 3: Set up environment
163export SNAP="$MOUNT_DIR"
164export SNAP_NAME="$SNAP_NAME"
165export SNAP_VERSION="$VERSION"
166export SNAP_REVISION="$SNAP_REVISION"
167export SNAP_ARCH=$(uname -m)
168
169export SNAP_DATA="/var/snap/$SNAP_NAME/$SNAP_REVISION"
170export SNAP_COMMON="/var/snap/$SNAP_NAME/common"
171export SNAP_USER_DATA="$HOME/snap/$SNAP_NAME/$SNAP_REVISION"
172export SNAP_USER_COMMON="$HOME/snap/$SNAP_NAME/common"
173
174export SNAP_INSTANCE_NAME="$SNAP_NAME"
175export SNAP_INSTANCE_KEY=""
176export SNAP_LIBRARY_PATH="/var/lib/snapd/lib/gl:/var/lib/snapd/lib/gl32"
177export SNAP_UID=$(id -u)
178export SNAP_EUID=$(id -u)
179export SNAP_REAL_HOME="$HOME"
180
181# Step 4: Create data directories
182echo "Creating data directories..."
183mkdir -p "$SNAP_USER_DATA"
184mkdir -p "$SNAP_USER_COMMON"
185
186# For system directories, you might need sudo:
187# sudo mkdir -p "$SNAP_DATA"
188# sudo mkdir -p "$SNAP_COMMON"
189
190# Step 5: Run the application
191echo "Running snap application..."
192cd "$SNAP"
193exec ./$COMMAND "$@"
194```
195
196## What Will Be Missing
197
198Running snaps manually without the snapd ecosystem means you lose **significant functionality**:
199
200### 1. **No Confinement/Security**
201
202- **AppArmor profiles**: Not loaded, snap runs without MAC (Mandatory Access Control)
203- **Seccomp filters**: Not applied, snap has access to all syscalls
204- **Device cgroup**: Not configured, snap can access all devices
205- **Mount namespaces**: Not created, snap sees the entire host filesystem
206- **User namespaces**: Not used for privilege separation
207
208**Impact**: The snap runs with **no security boundaries**. A malicious snap could compromise your entire system.
209
210### 2. **No Automatic Updates**
211
212- No connection to Snap Store
213- No background update checks
214- No automatic snap refreshes
215- Must manually download and replace snap files
216
217**Impact**: You must manually track and install updates. Security patches won't be applied automatically.
218
219### 3. **No Interface Connections**
220
221- No access to plugs and slots system
222- Cannot connect to system resources like:
223 - Network Manager
224 - Audio (PulseAudio/PipeWire)
225 - Bluetooth
226 - Hardware devices (cameras, printers, etc.)
227- No automatic permission management
228
229**Impact**: The snap may fail if it expects interface connections. Many features won't work.
230
231### 4. **No Services**
232
233- Cannot run systemd services defined in the snap
234- No daemon management (start/stop/restart)
235- No socket activation
236- No timer-based execution
237- No automatic service restarts on failure
238
239**Impact**: Only command-line applications can be run. Background services, daemons, and scheduled tasks won't work.
240
241### 5. **No Mount Management**
242
243- No dynamic mount namespace setup
244- Layouts defined in snap.yaml won't be applied
245- No bind mounts for content sharing
246- No per-snap `/tmp` or other private mounts
247
248**Impact**: Applications expecting specific filesystem layouts will fail. Content interfaces won't work.
249
250### 6. **No State Management**
251
252- No tracking of installed snaps
253- No revision management
254- No rollback capability
255- No state.json or other snapd state files
256
257**Impact**: Cannot track what's installed, cannot rollback to previous versions.
258
259### 7. **No snapctl Integration**
260
261#### What is snapctl?
262
263`snapctl` is a command-line tool that snaps use to communicate with the snapd daemon. It's the bridge between a snap application and the snap system's runtime services. When a snap executes, snapd sets up environment variables (like `$SNAP_COOKIE`) that allow `snapctl` to authenticate and communicate with the snapd daemon over a Unix socket.
264
265#### Why is it needed?
266
267Snaps use `snapctl` for several critical functions:
268
2691. **Configuration Management**
270 - `snapctl get <key>` - Read configuration values
271 - `snapctl set <key>=<value>` - Write configuration values
272 - Configuration is stored by snapd and persists across snap updates
273
2742. **Service Management**
275 - `snapctl start/stop/restart <service>` - Control snap services
276 - Query service status
277 - Coordinate between multiple services in a snap
278
2793. **Hook Execution Context**
280 - Hooks (install, configure, pre-refresh, post-refresh, etc.) run with snapctl access
281 - Hooks use snapctl to validate configuration, set defaults, perform migrations
282 - Essential for snap lifecycle management
283
2844. **Health Checks**
285 - `snapctl set-health` - Report snap health status to snapd
286 - Used by monitoring and management tools
287
2885. **Connection Information**
289 - Query which interfaces are connected
290 - Get connection attributes and parameters
291
2926. **System Information**
293 - Query snap revision, version, architecture
294 - Get information about other installed snaps
295
296#### The Problem
297
298Without snapd running, `snapctl` has nowhere to connect. The command will fail with:
299
300```
301error: snapctl: cannot invoke snapctl operation commands from outside of a snap
302```
303
304Or if it tries to connect:
305
306```
307error: cannot communicate with server: connection refused
308```
309
310Many "enterprise" or complex snaps (like charmcraft, lxd, multipass, etc.) heavily rely on snapctl for configuration management. They expect to be able to read/write configuration through snapctl, which breaks completely without snapd.
311
312#### Detection Mechanism
313
314Snaps detect they're running under snapd via:
315- `$SNAP_COOKIE` environment variable (authentication token)
316- `$SNAP_CONTEXT` environment variable (legacy)
317- Unix socket at `/run/snapd.socket` or `/run/snapd-snap.socket`
318
319Libraries like `snaphelpers` (Python) automatically try to use snapctl and fail when it's unavailable.
320
321#### Workarounds
322
323**1. Mock Configuration Files**
324
325Create a fake configuration that the snap might read:
326
327```bash
328# Some snaps fall back to reading config files
329mkdir -p "$SNAP_DATA"
330cat > "$SNAP_DATA/config.json" << EOF
331{
332 "key": "value"
333}
334EOF
335```
336
337**Limitation**: Only works if the snap has fallback code paths. Most don't.
338
339**2. Stub snapctl Command**
340
341Create a fake `snapctl` that returns empty/default values:
342
343```bash
344#!/bin/bash
345# Fake snapctl that returns empty config
346case "$1" in
347 get)
348 echo "{}" # Return empty JSON config
349 ;;
350 set)
351 exit 0 # Pretend set succeeded
352 ;;
353 *)
354 exit 1
355 ;;
356esac
357```
358
359Add it to PATH before running the snap:
360
361```bash
362export PATH="/path/to/fake-snapctl:$PATH"
363```
364
365**Limitation**:
366- Very brittle, depends on what the snap expects
367- Doesn't work for snaps that validate responses
368- Can cause unexpected behavior if snap makes decisions based on config
369
370**3. Patch the Snap**
371
372For Python-based snaps using `snaphelpers`:
373
374```bash
375# Extract the snap
376unsquashfs -d my-snap my-snap.snap
377
378# Modify the code to skip snapctl
379# Example: patch snaphelpers to not use snapctl
380vim my-snap/lib/python3.12/site-packages/snaphelpers/_conf.py
381
382# Repack (requires snap-pack tool, part of snapd)
383snap pack my-snap/
384```
385
386**Limitation**:
387- Requires understanding the snap's internals
388- Must repack the snap (needs snapd tools!)
389- Breaks snap signatures
390- Must repeat for every snap update
391
392**4. Run with Environment Variables**
393
394Some snaps check for specific environment variables and disable features:
395
396```bash
397# Tell snap not to use snapctl (if supported)
398export SNAP_TESTING=1
399export IGNORE_SNAP_CONFIG=1
400
401# Run the snap
402./my-snap
403```
404
405**Limitation**: Only works if the snap explicitly supports these flags (rare).
406
407**5. Use Classic Confinement Snaps**
408
409Classic confinement snaps sometimes have less dependency on snapctl:
410
411```bash
412# Classic snaps might work better without snapd
413# But classic snaps typically need snapd to install anyway
414```
415
416**Limitation**: Classic snaps still often use snapctl.
417
418#### Real-World Example: charmcraft
419
420Charmcraft (as seen in the error above) uses the `craft-application` framework which uses `snaphelpers` for configuration. On startup, it tries:
421
422```python
423debug_mode = self.services.get("config").get("debug")
424```
425
426This internally calls:
427```python
428snapctl.run("get", "-d", "debug")
429```
430
431Which fails immediately without snapd.
432
433**Workaround**: Patch craft-application or set environment to skip config:
434
435```bash
436# This doesn't actually work for charmcraft, just illustrative
437export CRAFT_DEBUG=0 # Try to bypass config system
438```
439
440Unfortunately, charmcraft doesn't have a clean way to disable snapctl usage.
441
442#### Bottom Line
443
444**There is no general workaround for snapctl.** Each snap that uses it must be handled individually:
445
446- Simple snaps that only use snapctl for optional features might work
447- Complex snaps that depend on snapctl for core functionality will fail
448- The only real solution is to run snapd
449
450If you cannot run snapd, you must:
4511. Choose snaps that don't use snapctl (rare, mostly simple CLI tools)
4522. Patch the snap to remove snapctl dependencies (difficult)
4533. Use alternative packaging formats (AppImage, static binaries, containers)
454
455**Snaps that commonly use snapctl** (avoid these without snapd):
456- charmcraft, lxd, multipass, microk8s
457- Most "platform" or "enterprise" snaps
458- Snaps with complex configuration
459- Snaps with services and hooks
460
461**Snaps that might work** (less snapctl usage):
462- Simple CLI tools: shellcheck, yq, jq-alike tools
463- Static binaries wrapped as snaps
464- Snaps with no configuration or hooks
465
466### 8. **No Command Chain Support**
467
468- Command chains defined in snap.yaml won't execute
469- No wrapper script execution
470- No environment setup by command chains
471
472**Impact**: Some snaps rely on command chains for initialization. These will fail silently.
473
474### 9. **No User Data Migration**
475
476- No automatic migration of user data between revisions
477- No XDG directory symlink management
478- No exposed home directory setup
479
480**Impact**: User data management is manual and error-prone.
481
482### 10. **No Desktop Integration**
483
484- Desktop files not registered with system
485- Application won't appear in application menus
486- Icons not installed to system locations
487- MIME type associations not created
488- URL handlers not registered
489
490**Impact**: The application won't integrate with your desktop environment.
491
492### 11. **No Parallel Installs**
493
494- Cannot install multiple versions or instances
495- No instance key support
496- Name conflicts if multiple versions exist
497
498**Impact**: Limited to one "installation" per snap name.
499
500### 12. **No Graphics Acceleration**
501
502- No automatic graphics driver mounting
503- NVIDIA/AMD drivers not available inside snap
504- No access to `/var/lib/snapd/lib/gl`
505
506**Impact**: Graphics applications may fail or run with software rendering only.
507
508### 13. **No Snap-Confine Isolation**
509
510- No PID 1 mount namespace tricks
511- No `/snap` directory bind mounts
512- No namespace file creation in `/run/snapd/ns/`
513- No cgroup tracking for process management
514
515**Impact**: Multiple issues with process isolation and management.
516
517### 14. **No Seed/Preseeding Support**
518
519- Cannot participate in system seeding
520- Not part of system image creation
521- Cannot be part of Ubuntu Core images
522
523**Impact**: Cannot be used in embedded or appliance scenarios.
524
525### 15. **No Assertions/Signing Verification**
526
527- Snap signatures not verified
528- Snap declarations not checked
529- No trust chain validation
530- No revision authority verification
531
532**Impact**: Cannot verify snap authenticity or integrity. Could run tampered snaps.
533
534## Additional Caveats
535
536### Environment Variables
537
538The snap may expect additional environment variables:
539
540- `$SNAP_COOKIE` - For snapctl communication (won't work)
541- `$SNAP_CONTEXT` - Legacy snapctl communication (won't work)
542- `$XDG_RUNTIME_DIR` - Should point to snap-specific runtime dir
543- `$HOME` - Typically overridden to `$SNAP_USER_DATA` for strict confinement
544
545### Library Dependencies
546
547- The snap may have dependencies on system libraries
548- Without proper `SNAP_LIBRARY_PATH` setup, libraries won't be found
549- Classic confinement snaps expect host libraries
550
551### Path Issues
552
553- The snap may expect to run from specific mount points (`/snap/...`)
554- Some snaps hardcode paths assuming standard snap mount locations
555- Environment variable expansion (`$SNAP`, etc.) must work correctly
556
557### Permissions
558
559- File permissions inside the snap may not match your user
560- SELinux/AppArmor contexts won't be correct
561- Extended attributes may cause issues
562
563## Use Cases for Manual Snap Running
564
565Despite all the limitations, manual snap running might be useful for:
566
5671. **Educational purposes**: Understanding how snaps work internally
5682. **Debugging**: Examining snap contents without installing
5693. **Offline/air-gapped systems**: Where snapd cannot be installed
5704. **Emergency recovery**: Running tools from snaps when system is broken
5715. **Embedded systems**: Very constrained environments without systemd
5726. **Research**: Analyzing snap behavior and structure
573
574## Alternative: Snap-Confine Without Snapd
575
576If you need **some** confinement without full snapd, you could theoretically:
577
5781. Compile `snap-confine` standalone
5792. Write AppArmor profiles manually
5803. Create seccomp filters manually
5814. Set up mount namespaces manually
5825. Configure device cgroups manually
583
584This is **extremely complex** and not recommended. You're essentially reimplementing snapd.
585
586## Recommended Alternatives
587
588If you're in a restricted environment, consider these alternatives:
589
590### 1. Static Binaries
591- Use statically compiled binaries instead
592- Tools like Go produce static binaries
593- No runtime dependencies
594
595### 2. AppImage
596- Self-contained application bundles
597- Don't require systemd
598- Mount via FUSE (which has its own requirements)
599
600### 3. Flatpak
601- Also uses namespaces but has different requirements
602- Can run without systemd (with limitations)
603
604### 4. Docker/Podman Containers
605- Better isolation without snapd
606- More mature tooling for constrained environments
607
608### 5. Traditional Packages
609- Distribution packages (deb, rpm)
610- Follows system conventions
611- Better integration without systemd
612
613## Conclusion
614
615Running snaps without snapd and systemd is possible for **very simple, statically-linked command-line tools**, but you lose:
616
617- All security features (AppArmor, seccomp, namespaces)
618- All management features (updates, rollback, configuration)
619- All integration features (interfaces, desktop integration, services)
620- All snapd-specific functionality
621
622The effort required grows exponentially with snap complexity. For anything beyond basic CLI tools, the missing functionality makes this approach impractical.
623
624**Bottom line**: If you can't install systemd, snaps are not the right packaging format for your use case. Consider the alternatives listed above.
625
626## References
627
628- [Snapd Source Code](https://github.com/canonical/snapd)
629- [SquashFS Documentation](https://www.kernel.org/doc/Documentation/filesystems/squashfs.txt)
630- Key snapd components:
631 - `snap-confine`: Confinement helper (requires compiled binary)
632 - `snap-exec`: Execution helper inside confinement
633 - `snap-update-ns`: Mount namespace manager
634 - `snapd` daemon: State management and API server
635
636## Warning
637
638⚠️ **This guide is for educational purposes only.** Running snaps without proper confinement is a security risk. The snap has full access to your system, just like any other executable. Only run snaps from trusted sources, and never use this approach in production environments.