Skip to the content.

Headless OrcaSlicer Setup for Snapmaker U1

Use OrcaSlicer from the command line to slice STL and 3MF files for a Snapmaker U1 with no GUI. This guide covers the correct upstream build, the three-profile load order, Linux and Windows setup, profile-inheritance pitfalls, orientation review, thumbnail injection, and G-code validation.

← Back to the README


Headless slicing setup (no GUI / scripted)

Use this if you’re slicing from CLI in a container, CI pipeline, or agent workflow.

Use upstream OrcaSlicer, not the Snapmaker fork

Important: use upstream OrcaSlicer v2.4.0+, not Snapmaker’s fork. Snapmaker upstreamed the U1 vendor profile into upstream OrcaSlicer 2.4.0, so it has full U1 support — and its CLI is the better-supported headless path. The Snapmaker fork’s Windows CLI has been observed to segfault when slicing with these profiles (verified snapmaker-orca v2.3.4 Windows can exit code -1073741819 on some models).

Install — Linux (extracted AppImage)

# Download upstream OrcaSlicer Linux AppImage
wget https://github.com/OrcaSlicer/OrcaSlicer/releases/download/v2.4.0/OrcaSlicer_Linux_AppImage_Ubuntu2404_V2.4.0.AppImage \
  -O ~/orcaslicer.AppImage
chmod +x ~/orcaslicer.AppImage

# Extract instead of mounting (containers without FUSE)
mkdir -p ~/orcaslicer-install && cd ~/orcaslicer-install
~/orcaslicer.AppImage --appimage-extract
# Creates ./squashfs-root/

# Some minimal distros are missing GUI/runtime libs Orca expects.
# If you hit "libGL.so.1 not found" or similar:
mkdir local-libs && cd local-libs
apt-get download libgl1 libegl1 libxkbcommon0 libwayland-client0 libnss3 \
                 libasound2 libgtk-3-0 libdbus-1-3 libsecret-1-0
for d in *.deb; do dpkg-deb -x "$d" .; done

Install — Windows (portable zip, no installer needed)

# Download upstream OrcaSlicer Windows portable
Invoke-WebRequest -Uri https://github.com/OrcaSlicer/OrcaSlicer/releases/download/v2.4.0/OrcaSlicer_Windows_V2.4.0_portable.zip `
    -OutFile $env:TEMP\OrcaSlicer.zip
Expand-Archive $env:TEMP\OrcaSlicer.zip -DestinationPath $env:TEMP\orca240

# The CLI binary lives at $env:TEMP\orca240\orca-slicer.exe

Slice a single STL — the 3-profile chain

Headless slicing needs three profiles in a specific load order:

  1. Machine — the printer definition (this repo bundles a flattened standalone copy)
  2. Process — layer height, walls, infill, supports
  3. Filament — material, temps, retraction

Pass each profile via its own --load-settings flag (not one flag with semicolon-separated paths). Both forms are documented in OrcaSlicer, but the dual-flag form is the one verified-working in our test runs (Hermes Windows) and avoids quoting foot-guns on PowerShell.

# Linux
ORCA=$HOME/orcaslicer-install
PROFILES=$(pwd)/profiles

LD_LIBRARY_PATH="$ORCA/local-libs/usr/lib/x86_64-linux-gnu:$ORCA/squashfs-root/usr/lib:$ORCA/squashfs-root/usr/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH" \
  $ORCA/squashfs-root/bin/orca-slicer \
  --load-settings "$PROFILES/machine/snapmaker_u1_0_4_nozzle.json" \
  --load-settings "$PROFILES/community_merged_016_optimal_u1_textured_pei.json" \
  --load-filaments "$PROFILES/community_generic_petg_u1_textured_pei.json" \
  --outputdir ./output \
  --slice 0 \
  my_model.stl
# Windows (PowerShell)
& "$env:TEMP\orca240\orca-slicer.exe" `
  --load-settings "profiles\machine\snapmaker_u1_0_4_nozzle.json" `
  --load-settings "profiles\community_merged_016_optimal_u1_textured_pei.json" `
  --load-filaments "profiles\community_generic_petg_u1_textured_pei.json" `
  --outputdir .\output `
  --slice 0 `
  my_model.stl

Sliced G-code lands in ./output/plate_1.gcode.

Why the bundled machine profile? Upstream Orca’s bundled U1 profile inherits from fdm_U1fdm_toolchangerfdm_klipper. Loading the bundled vendor copy via CLI requires Orca to find every parent in its install resources, which is fragile across platforms. The repo’s profiles/machine/snapmaker_u1_0_4_nozzle.json is fully flattened — every inherited field merged into one standalone file. Headless CLI sees one file, gets the complete machine definition, no resolution magic needed. Derived from upstream OrcaSlicer’s Snapmaker/machine/ vendor profiles (AGPL-3.0, contributed by Snapmaker).

Headless profile-loading pitfall (READ THIS)

OrcaSlicer’s bundled Snapmaker process profiles do not always resolve inheritance correctly via CLI. Symptoms seen in testing:

Workaround: use profiles whose inheritance chain CLI can resolve. Three good options today:

Pre-print orientation review

Before you slice, ask the question every operator forgets: is this the right orientation, and where will it need supports? The orientation renderer gives you a 4-panel image showing isometric / front / side / top views with all downward-facing triangles highlighted in orange — those are the faces a slicer will warn about.

pip install Pillow numpy  # one-time (same deps as the thumbnail tool)

python3 tools/render_stl_orientation.py model.stl \
    --out orientation.png \
    --title "Orbital sander vacuum attachment"

Output is a single PNG with header text (bounding-box dims, Z range, count of overhang triangles) and the 4 views. Tunable via --overhang-threshold if your slicer/material is more or less paranoid than the default (-0.3 ≈ 17° below horizontal).

Add a Snapmaker-app preview thumbnail

OrcaSlicer’s CLI path doesn’t render thumbnails (GUI-only — verified with --debug 5, no GL/xvfb workaround helps). Without them, the Snapmaker app shows a generic icon for every print. Use the included tool to splice PrusaSlicer/Orca-format thumbnail blocks into the G-code post-slice:

pip install Pillow numpy  # one-time

python3 tools/gcode_inject_thumbnail.py \
    --stl my_model.stl --gcode output/plate_1.gcode \
    --sizes 48x48,300x300 --in-place

Runs an isometric projection of the STL through PIL (Lambertian-shaded triangles, painter’s algorithm), base64-encodes the PNGs, and splices ; thumbnail begin … ; thumbnail end blocks into the G-code header. Idempotent — re-running replaces existing blocks, not stacks them. Moonraker + Snapmaker app parse them as standard previews.

Validate G-code before upload

grep -E '^; (filament_type|layer_height|first_layer_temperature|bed_temperature) ' output/plate_1.gcode

Expected output for the merged 0.16 Optimal PETG profile:

; filament_type = PETG
; layer_height = 0.16
; first_layer_temperature = 255
; bed_temperature = 80

If any of those are wrong, the CLI didn’t load your profiles correctly — fix before uploading.


← Back to the README