Sign inSign up

exasol/nano

By exasol

Updated about 1 month ago

Exasol Core SQL engine, single-node for dev, testing, edge & embedded analytics.

Image
Artifact
Developer tools
Data science
Databases & storage
2

6.4K

exasol/nano repository overview

Exasol Nano for Docker/Podman

This Docker Hub guide shows the shortest path to a local Exasol Nano container and then lists optional mounts for SLCs, JDBC drivers, and BucketFS content.

All Nano examples use docker.io/exasol/nano:latest and include the recommended runtime limits:

  • --shm-size=512mb
  • --pids-limit=-1

Unless noted otherwise, commands use podman. Docker users can usually replace podman with docker; SLC/UDF examples list Docker-specific security options separately. For reproducible automation, prefer a pinned published image tag instead of latest.

Start here

  1. Start Nano with a persistent /exa volume.
  2. Wait until the logs print Database is now up and running!.
  3. Connect to 127.0.0.1:8563 with user sys and the initial SYS password for that runtime.
podman run --rm -it --name exanano \
  --shm-size=512mb \
  --pids-limit=-1 \
  -p 8563:8563 \
  -v exanano-data:/exa \
  docker.io/exasol/nano:latest

Stop an interactive run with Ctrl+C / Ctrl+D.

What /exa contains

Mount /exa to keep database state between container runs. Persisted data includes:

  • config: /exa/exasol.conf
  • logs: /exa/logs
  • storage: /exa/storage
  • SLCs: /exa/slc
  • JDBC drivers: /exa/jdbc
  • BucketFS content: /exa/bucketfs
  • BucketFS config: /exa/bucketfs.conf

Common run commands

Show Nano help
podman run --rm -it \
  --shm-size=512mb \
  --pids-limit=-1 \
  -v exanano-data:/exa \
  docker.io/exasol/nano:latest init help
Use a host directory instead of a named volume
mkdir -p "$PWD/exa"
podman run --rm -it --name exanano \
  --shm-size=512mb \
  --pids-limit=-1 \
  -p 8563:8563 \
  -v "$PWD/exa:/exa" \
  docker.io/exasol/nano:latest

Use an absolute host path and make sure it is writable by your rootless user.

Run detached
podman run -d --name exanano \
  --shm-size=512mb \
  --pids-limit=-1 \
  -p 8563:8563 \
  -v exanano-data:/exa \
  docker.io/exasol/nano:latest

Stop and remove the detached container:

podman stop -t 60 exanano && podman rm exanano
Set DB parameters on start

Pass DB parameters with init params='k=v ...'. On an empty /exa, Nano bootstraps the runtime, applies the parameters, and starts the DB. The values are persisted in /exa/exasol.conf, so later restarts can usually omit the extra init params=... arguments.

podman run --rm -it --name exanano \
  --shm-size=512mb \
  --pids-limit=-1 \
  -p 8563:8563 \
  -v exanano-data:/exa \
  docker.io/exasol/nano:latest init params='dbram=4096 nrOfCores=8'
Set the initial SYS password on first deployment

For CI/CD or secret-mount workflows, provide the initial SYS password from a file during the first deployment only:

podman run --rm -it --name exanano \
  --shm-size=512mb \
  --pids-limit=-1 \
  -p 8563:8563 \
  -v exanano-data:/exa \
  -v "$PWD/sys_password:/run/secrets/sys_password:ro" \
  docker.io/exasol/nano:latest init sys_password_file=/run/secrets/sys_password

For an interactive first deployment, use:

podman run --rm -it --name exanano \
  --shm-size=512mb \
  --pids-limit=-1 \
  -p 8563:8563 \
  -v exanano-data:/exa \
  docker.io/exasol/nano:latest init sys_password_prompt

Notes:

  • these options work only on the first deployment of a fresh /exa runtime
  • after first deployment, you can also change the SYS password with SQL ALTER USER
  • if you do not provide either option, the default initial SYS password is exasol

Connect and run your first query

Nano generates a self-signed TLS certificate by default, and the SQL port accepts TLS connections only. When startup is finished, the container logs print:

  • Database is now up and running!
  • an exaplus example command that includes the SQL port and certificate fingerprint
  • Enter the SYS password when prompted.

Watch startup logs with:

podman logs -f exanano

Freshly initialized Nano runtimes use:

  • user: sys
  • password: the initial SYS password chosen for that runtime

If you do not provide an explicit initial SYS password during first deployment, the default is exasol.

For local quick starts, keep the SQL port on localhost and do not expose this login to untrusted networks. To bind the SQL port to localhost only, replace -p 8563:8563 in the run commands with -p 127.0.0.1:8563:8563.

Quick local smoke test with Python

Install pyexasol in a virtual environment:

python3 -m venv .venv
. .venv/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install pyexasol

Then run a first query. This local example disables certificate verification for convenience; do not use that setting for remote or untrusted networks. If your client supports certificate fingerprints, prefer the fingerprint shown in the Nano startup logs.

NANO_SYS_PASSWORD=${NANO_SYS_PASSWORD:-exasol} python3 - <<'PY'
import os
import ssl
import pyexasol

conn = pyexasol.connect(
    dsn="127.0.0.1:8563",
    user="sys",
    password=os.environ["NANO_SYS_PASSWORD"],
    encryption=True,
    websocket_sslopt={"cert_reqs": ssl.CERT_NONE},
)

print(conn.execute("SELECT 1").fetchall())
conn.execute("CREATE SCHEMA IF NOT EXISTS DEMO")
conn.execute("OPEN SCHEMA DEMO")
conn.execute("CREATE OR REPLACE TABLE T (ID INT)")
conn.execute("INSERT INTO T VALUES (1), (2)")
print(conn.execute("SELECT * FROM T ORDER BY ID").fetchall())
conn.close()
PY
JDBC / GUI tools

Connection basics:

  • host: 127.0.0.1
  • port: 8563
  • user: sys
  • password: the initial SYS password for that runtime
  • JDBC URL: jdbc:exa:localhost:8563

If your SQL client supports certificate fingerprints, use the fingerprint from the Nano startup logs. Otherwise configure the client to trust the generated self-signed certificate, or use the client-specific no-cert-check option only for local testing.

Official Exasol documentation:

  • docs home: https://docs.exasol.com/db/latest/
  • SQL reference: https://docs.exasol.com/db/latest/sql_reference.htm

Install packaged SLCs for UDFs

Nano is SLC-only. To install packaged language containers (java, python, r), run init slc install=all once for the /exa runtime.

Podman

For packaged SLC/UDF execution with Podman, add --security-opt unmask=ALL:

podman run --rm -it --name exanano \
  --security-opt unmask=ALL \
  --shm-size=512mb \
  --pids-limit=-1 \
  -p 8563:8563 \
  -v exanano-data:/exa \
  docker.io/exasol/nano:latest init slc install=all
Docker rootless

For packaged SLC/UDF execution with rootless Docker, add --security-opt seccomp=unconfined and --security-opt systempaths=unconfined:

docker run --rm -it --name exanano \
  --security-opt seccomp=unconfined \
  --security-opt systempaths=unconfined \
  --shm-size=512mb \
  --pids-limit=-1 \
  -p 8563:8563 \
  -v exanano-data:/exa \
  docker.io/exasol/nano:latest init slc install=all

On some rootful Docker hosts with AppArmor enabled, UDF execution may additionally require --security-opt apparmor=unconfined.

Useful SLC arguments:

  • install selected languages: init slc install=java,python
  • install a specific compatible release: init slc install=all version=11.1.1
  • replace an existing SLC install: add reinstall=1

For later runs that use SLC UDFs, reuse the same runtime-specific security and resource options.

Keep SLC files in a separate host directory

Mount /exa/slc separately if you want to reuse or inspect installed SLC files independently from the rest of the Nano runtime data:

mkdir -p "$PWD/exa" "$PWD/slc"
podman run --rm -it --name exanano \
  --security-opt unmask=ALL \
  --shm-size=512mb \
  --pids-limit=-1 \
  -p 8563:8563 \
  -v "$PWD/exa:/exa" \
  -v "$PWD/slc:/exa/slc" \
  docker.io/exasol/nano:latest init slc install=all

If you already have a prepared SLC root from a previous run or another host, mount it the same way at /exa/slc.

Mount a JDBC driver

JDBC drivers live below /exa/jdbc/<DRIVERNAME>/. A driver directory usually contains settings.cfg and one or more JDBC jar files.

Example host directory:

$PWD/SQLITE/
├── settings.cfg
└── sqlite-jdbc-3.46.1.3.jar

Example settings.cfg:

DRIVERNAME=SQLITE
JAR=sqlite-jdbc-3.46.1.3.jar
DRIVERMAIN=org.sqlite.JDBC
FETCHSIZE=100000
INSERTSIZE=-1
PREFIX=jdbc:sqlite:
NOSECURITY=YES

Mount it read-only:

podman run --rm -it --name exanano \
  --shm-size=512mb \
  --pids-limit=-1 \
  -p 8563:8563 \
  -v exanano-data:/exa \
  -v "$PWD/SQLITE:/exa/jdbc/SQLITE:ro" \
  docker.io/exasol/nano:latest

If the JDBC driver is used from SLC/UDF code, combine this mount with the SLC runtime options from the previous section.

Mount BucketFS content

Nano reconciles user BucketFS entries from /exa/bucketfs/<bucketfs>/<bucket>/... and exposes them to UDFs at /buckets/<bucketfs>/<bucket>/....

If you already bind the whole runtime root

Create content directly below the host runtime directory:

mkdir -p "$PWD/exa/bucketfs/testing/publicbucket"
printf 'hello from public\n' > "$PWD/exa/bucketfs/testing/publicbucket/README.md"

Nano reconciles /exa/bucketfs.conf automatically. Newly discovered buckets start as public buckets.

If you prefer a dedicated BucketFS host directory

Layer a second bind mount below /exa:

podman run --rm -it --name exanano \
  --shm-size=512mb \
  --pids-limit=-1 \
  -p 8563:8563 \
  -v exanano-data:/exa \
  -v "$PWD/bucketfs:/exa/bucketfs" \
  docker.io/exasol/nano:latest

Then create host content under the mounted directory:

mkdir -p "$PWD/bucketfs/testing/publicbucket"
printf 'hello from public\n' > "$PWD/bucketfs/testing/publicbucket/README.md"

Reconcile notes:

  • no Nano restart is required when you create a new bucket directory
  • Exanano polls and reconciles /exa/bucketfs.conf automatically
  • updated visibility is picked up on the next UDF execution / sandbox initialization
  • current validation documents read access parity; do not rely on writes through /buckets/... as a supported feature
Make a discovered bucket private

The copy/paste path for private buckets is to use a host bind mount for /exa, for example -v "$PWD/exa:/exa". This gives you direct access to both the bucket content and the generated bucketfs.conf file.

Create the bucket content:

mkdir -p "$PWD/exa/bucketfs/testing/secure"
printf 'secret data\n' > "$PWD/exa/bucketfs/testing/secure/README.md"

Wait until Nano discovers the bucket and writes the config entry:

while ! grep -q ' testing secure ' "$PWD/exa/bucketfs.conf" 2>/dev/null; do
  sleep 1
done

Convert exactly that discovered bucket to private. This example uses password test1 and base64-encodes it automatically:

nano_bucketfs_make_private() {
  local bucketfs_cfg="$1"
  local runtime_root="$2"
  local bucketfs_name="$3"
  local bucket_name="$4"
  local password="$5"

  python3 - "$bucketfs_cfg" "$runtime_root" "$bucketfs_name" "$bucket_name" "$password" <<'PY'
import base64
import os
import sys
from pathlib import Path

cfg_path = Path(sys.argv[1])
runtime_root = sys.argv[2].rstrip('/')
fs_name = sys.argv[3]
bucket_name = sys.argv[4]
password_b64 = base64.b64encode(sys.argv[5].encode('utf-8')).decode('ascii')

matches = 0
out_lines = []
for line in cfg_path.read_text(encoding='utf-8').splitlines():
    stripped = line.strip()
    if not stripped or stripped.startswith('#'):
        out_lines.append(line)
        continue
    tokens = stripped.split()
    if len(tokens) == 6 and tokens[1] == fs_name and tokens[2] == bucket_name:
        matches += 1
        out_lines.append(
            f"{runtime_root}/bucketfs/{fs_name}/{bucket_name} "
            f"{fs_name} {bucket_name} /buckets/{fs_name}/{bucket_name} {password_b64} -"
        )
    else:
        out_lines.append(line)

if matches != 1:
    raise SystemExit(
        f"Expected exactly one entry for {fs_name}/{bucket_name} in {cfg_path}, found {matches}"
    )

tmp_path = cfg_path.with_name(cfg_path.name + '.tmp')
tmp_path.write_text(''.join(line + '\n' for line in out_lines), encoding='utf-8')
os.replace(tmp_path, cfg_path)
print(f"Updated {cfg_path} for private bucket {fs_name}/{bucket_name}")
PY
}

nano_bucketfs_make_private \
  "$PWD/exa/bucketfs.conf" \
  /exa \
  testing secure test1

Important: the config line must use the container/runtime path /exa/bucketfs/testing/secure, not the host path $PWD/exa/bucketfs/testing/secure.

Create the matching SQL connection:

CREATE CONNECTION bfs_secure
TO 'bucketfs:testing/secure'
IDENTIFIED BY 'test1';

Compose

The following compose.yaml is the compact starting point for Docker Compose or podman-compose. It starts Nano in the foreground with persisted data and the SQL port exposed.

services:
  exanano:
    image: docker.io/exasol/nano:latest
    container_name: exanano
    ports:
      - "8563:8563"
    volumes:
      - exanano-data:/exa
    shm_size: "512mb"
    pids_limit: -1
    stdin_open: true
    tty: true

volumes:
  exanano-data:

Start:

docker compose up

or:

podman-compose up

Stop:

docker compose down

or:

podman-compose down

Useful compose additions:

    # Use host directories instead of the named /exa volume.
    volumes:
      - ./exa:/exa
      - ./SQLITE:/exa/jdbc/SQLITE:ro
      - ./bucketfs:/exa/bucketfs
    # Podman SLC/UDF execution.
    security_opt:
      - unmask=ALL
    command: ["init", "slc", "install=all"]
    # Docker rootless SLC/UDF execution.
    security_opt:
      - seccomp=unconfined
      - systempaths=unconfined
    command: ["init", "slc", "install=all"]

On rootful Docker hosts with AppArmor enabled, also add apparmor=unconfined to security_opt.

Collect logs for support

Nano stores persistent logs below /exa/logs.

If you used a host bind mount for /exa

Create a compressed archive directly from the host directory:

tar -C "$PWD/exa" -czf exanano-logs.tgz logs
If you used a named volume for /exa

Create a temporary container, mount the Nano volume read-only, and write the archive to the current directory:

podman run --rm \
  --shm-size=512mb \
  --pids-limit=-1 \
  -v exanano-data:/exa:ro \
  -v "$PWD:/out" \
  docker.io/library/busybox:latest \
  sh -lc 'tar -C /exa -czf /out/exanano-logs.tgz logs'

If you use Docker, the same command works with docker run instead of podman run.

If the container is still present, also capture the container stdout/stderr stream:

podman logs exanano > exanano-container.log 2>&1

If you use Docker, replace podman logs with docker logs.

Most common problems

  • startup fails because /dev/shm is too small:
    • this guide uses --shm-size=512mb for normal runs
    • the startup preflight requires about 60 MiB available under /dev/shm
  • data is lost between runs:
    • mount /exa
  • SLC/UDF setup fails inside the container runtime:
    • use the runtime-specific security options shown above
    • on rootful Docker hosts with AppArmor enabled, also try --security-opt apparmor=unconfined
  • JDBC driver is not found:
    • mount it below /exa/jdbc/<DRIVERNAME> and include settings.cfg
  • a SQL client fails with a self-signed-certificate / certificate-verification error:
    • Nano uses TLS by default
    • use the certificate fingerprint printed in the startup logs when your client supports it
    • for a local smoke test, use the client-specific no-cert-check / disable-verification option
  • a BucketFS bucket does not show up in UDFs:
    • create it below /exa/bucketfs/<bucketfs>/<bucket>
    • wait for Exanano to reconcile /exa/bucketfs.conf
    • for private buckets, make sure the config line uses /exa/..., not the host path

Other ways to get Nano

Download the .run package

Published .run packages are stored in Docker Hub as ORAS artifacts with immutable tags matching the file name:

oras pull docker.io/exasol/nano:exasol-nano-db-<version>-<x86_64|aarch64>.run
chmod +x ./exasol-nano-db-<version>-<x86_64|aarch64>.run
./exasol-nano-db-<version>-<x86_64|aarch64>.run -- help

Without a local ORAS install, use the ORAS container image from the current directory:

podman run --rm \
  -v "$PWD:$PWD" -w "$PWD" \
  ghcr.io/oras-project/oras:v1.2.3 \
  pull docker.io/exasol/nano:exasol-nano-db-<version>-<x86_64|aarch64>.run

The pulled file lands in the current directory, same as with a local oras pull.

Public pulls do not require login. Pushes and private pulls require authentication. curl can download OCI blobs through the registry API, but ORAS is the supported path.

Tag summary

Content type

Image

Digest

sha256:459934930

Size

136.3 MB

Last updated

about 1 month ago

docker pull exasol/nano