Which setup do I have?
saneless is deployed in one of three shapes. Find yours below before you install
anything -- the shapes differ in whether you need saned and what you set
SANELESS_SCANNER__HOST to, and picking the wrong one costs you an hour.
The one rule about USB
Only a bare-metal install enumerates a locally attached USB scanner directly, through libsane on that machine.
A container never touches the USB bus. It always reaches a scanner over the
SANE network protocol -- including a saned that runs on the container's own
host. This is why saneless needs no --privileged flag and no device mapping:
saned owns the scanner, saneless talks to saned.
Shape 1 -- Bare metal, scanner on this machine
How to tell: scanimage -L on this machine lists your scanner, and you are not
using Docker.
This is the only shape where a locally attached USB scanner works with no saned
and no scanner.host. Install the SANE headers, install saneless, and scan:
saneless devices
saneless scan --profile default
Leave scanner.host empty. See Install on Bare Metal.
Shape 2 -- Container, scanner attached to the container's own host
How to tell: the scanner's cable goes into the same box that runs Docker.
The container cannot see that scanner directly, so you still need saned running
on that box, and SANELESS_SCANNER__HOST pointing at it -- either
host.docker.internal or the host's LAN address.
services:
saneless:
image: ghcr.io/kdknigga/saneless:0.2
stop_grace_period: 90s
ports:
- "8080:8080"
volumes:
- ./config:/etc/saneless
- saneless-data:/var/lib/saneless
environment:
- TZ=America/Chicago
- SANELESS_SCANNER__HOST=host.docker.internal
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
saneless-data:
docker run -d --name saneless -p 8080:8080 \
--stop-timeout 90 \
--add-host=host.docker.internal:host-gateway \
-v "$(pwd)/config:/etc/saneless" \
-v saneless-data:/var/lib/saneless \
-e SANELESS_SCANNER__HOST=host.docker.internal \
ghcr.io/kdknigga/saneless:0.2
On Linux Docker Engine, host.docker.internal resolves only because the
host-gateway line above maps it to the host; Docker Desktop provides the name
by itself.
saned refuses every client its /etc/sane.d/saned.conf does not list, and the
container connects from an address on its Docker network, not from the host's LAN
address. Print that network's subnet:
docker network inspect <project>_default --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'
Compose names the network after the project, which defaults to the name of
the directory holding docker-compose.yml: a project in saneless/ gets
saneless_default.
docker network inspect bridge --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'
Add the subnet it prints, such as 172.17.0.0/16, as a line of its own in
/etc/sane.d/saned.conf on this host. See
Scanner Host Discovery
if the Scanner row still says the host is refusing this machine.
Shape 3 -- Container, scanner on another machine
How to tell: the scanner is plugged into a different machine that runs saned,
or it speaks SANE over the network itself.
Identical to shape 2 apart from the address: point SANELESS_SCANNER__HOST at the
machine that has the scanner.
services:
saneless:
image: ghcr.io/kdknigga/saneless:0.2
stop_grace_period: 90s
ports:
- "8080:8080"
volumes:
- ./config:/etc/saneless
- saneless-data:/var/lib/saneless
environment:
- TZ=America/Chicago
- SANELESS_SCANNER__HOST=192.168.1.50
volumes:
saneless-data:
docker run -d --name saneless -p 8080:8080 \
--stop-timeout 90 \
-v "$(pwd)/config:/etc/saneless" \
-v saneless-data:/var/lib/saneless \
-e SANELESS_SCANNER__HOST=192.168.1.50 \
ghcr.io/kdknigga/saneless:0.2
Notes on the container lines above
- Mount the config directory, never the file inside it. saneless saves
generated profiles by renaming a temporary file over
saneless.toml, and a single-file bind mount makes that rename fail. Put yoursaneless.tomlinside./configand keep the directory writable. - The container's port is fixed at 8080. Change the left-hand half of the
mapping to serve it elsewhere, for example
-p 8888:8080. - The image runs as UID 1000. If
id -uon your host reports something else, runsudo chown -R 1000:1000 ./configonce; a plainchownto another user fails without root. See Docker. --stop-timeout 90belongs on everydocker run. Docker kills a container 10 seconds after asking it to stop unless told otherwise, and a stop during a scan may need longer to keep the pages scanned so far. The shippeddocker-compose.ymlsets the same budget asstop_grace_period: 90s.- The data volume is part of the minimum -- without it the job database and any preserved scans vanish when the container is recreated.
Next: Quick Start, or, for multi-host and multi-scanner setups, Scanner Host Discovery (Containers).