Skip to content

Install steps 00-19

Run on the server as the admin login user (not root), after bash infra/deploy.sh from your machine. Run all with bash /opt/lake/scripts/install-all.sh, or pick steps: install-all.sh 06 07.

Summary:

Step Script Installs
00 00-make-user.sh system user lake
01 01-install-seaweed.sh SeaweedFS binary
02 02-install-postgres.sh PostgreSQL 16, serving DB
03 03-install-postgrest.sh PostgREST
03.5 03.5-make-seaweed-folder.sh /data/seaweed
04 04-install-seaweed-service.sh S3 service and buckets
05 05-install-nessie.sh Java 21, Nessie
06 06-install-trino.sh Java 25, Trino, CLI
06.5 06.5-fix-trino-harmless-warning.sh trino wrapper
07 07-install-spark.sh Spark, Iceberg jars
07.5 07.5-spark-s3a.sh S3A support
08 08-serving-link.sh Trino to PostgreSQL link, demo chain
09 09-install-nifi.sh NiFi
13 13-install-keycloak.sh Keycloak
14 14-install-registry.sh ingest DB and migrations
15 15-install-worker.sh the worker
16 16-retire-old-intake.sh one-off cleanup (not in install-all)
17 17-install-lakestatus.sh lakestatus command
18 18-install-jupyterhub.sh JupyterHub
19 19-protect-cluster-env.sh lock down cluster.env

Numbers 10-12 were used by the retired FastAPI intake and are intentionally unused.


Does: the whole build, in order, without asking questions. Preflight: cluster.env must exist and contain no CHANGE_ME; the SQL files and the Keycloak realm file must have been copied by deploy.sh. Behaviour: stops at the first failing step. A step is the number its script starts with. Not included on purpose: 05.5 (obsolete) and 16 (one-off). Ends with: systemctl is-active seaweed postgresql postgrest nessie trino spark-master spark-worker nifi keycloak lake-worker jupyterhub.

Does: creates the system user lake (no login shell, home /opt/lake) if missing. Why: every service runs as lake, so one account owns all files and none runs as root.

Does: downloads the SeaweedFS release SEAWEED_VERSION, checks SEAWEED_SHA256, installs weed to /usr/local/bin. Check: it prints weed version.

Does: adds the official PostgreSQL apt repository, installs PostgreSQL 16, writes tuning to /etc/postgresql/16/main/conf.d/lake.conf (listens on localhost; max_connections 200, shared_buffers 8 GB, effective_cache_size 24 GB, work_mem 64 MB, sized for about 128 GB of RAM), and creates the serving database with schema api, roles web_anon (no login) and authenticator. Needs: POSTGREST_AUTH_PASSWORD. Check: it logs in as authenticator and runs select 1. Tune: use roughly 25 % of RAM for shared_buffers on other machines.

Does: installs PostgREST (checksum checked), generates a JWT secret once into /etc/lake/postgrest/jwt.secret, writes /etc/lake/postgrest/postgrest.conf (localhost, port 3000, schema api, anonymous role web_anon) and the unit postgrest. Note: PostgREST is the earlier way to serve tables; new work uses the API’s published views. It stays installed for now (PostgREST).

Does: creates /data/seaweed owned by lake: the data directory for object storage.

Does: writes the S3 identity file /etc/lake/seaweed/s3.json (one identity with S3_ACCESS_KEY / S3_SECRET_KEY), the unit seaweed, creates the buckets raw and lake, and tests them. Command in the unit: weed server -dir=/data/seaweed -ip=127.0.0.1 -ip.bind=127.0.0.1 -volume.port=8082 -volume.max=0 -s3 -s3.port=8333 -s3.config=.... Check (built in): uploads and lists a test file, then tries a wrong key and expects it to be rejected.

Does: installs Java 21, creates the PostgreSQL role and database nessie, downloads the Nessie runner jar (hash checked) to /opt/nessie, writes /etc/lake/nessie/application.properties and the unit nessie. Config highlights: listens on 127.0.0.1:19120; version store JDBC2 in PostgreSQL; warehouse s3://lake/warehouse; S3 endpoint and keys from cluster.env. Check: curl on /api/v2/config and /iceberg/v1/config.

Obsolete. Step 05 now writes that configuration itself. Kept for history; not run.

Does: installs Java 25 under /opt/java25 (Trino only), extracts Trino TRINO_VERSION to /opt/trino-<version> with a symlink /opt/trino, writes /etc/lake/trino/{node,jvm,config}.properties and the catalog lake.properties (Iceberg through Nessie, path-style S3), the unit trino, and installs the Trino CLI as /usr/local/bin/trino. Config highlights: single node (coordinator + worker), port 8080, heap TRINO_HEAP, data in /data/trino. Check: curl /v1/info.

06.5 · 06.5-fix-trino-harmless-warning.sh

Section titled “06.5 · 06.5-fix-trino-harmless-warning.sh”

Does: replaces the trino wrapper so Java’s native-access warning does not clutter CLI output.

Does: installs Apache Spark SPARK_VERSION (SHA-512 checked) to /opt/spark-<version>, symlink /opt/spark; adds the Iceberg Spark runtime jar and the Iceberg AWS bundle (checked against Maven Central); writes /etc/lake/spark/spark-env.sh and spark-defaults.conf (catalog lake = Nessie, S3FileIO to SeaweedFS); creates units spark-master (port 7077, UI 8081) and spark-worker (UI 8083); installs the helper lake-spark-sql. Warning: this step rewrites spark-defaults.conf, so re-run 07.5 afterwards.

Does: adds the hadoop-aws jar matching Spark’s Hadoop version and an S3A settings block (between # BEGIN s3a and # END s3a markers, replaced on re-run) so Spark jobs can read s3a://raw/.... Needs: step 07. Needed by: the worker (15). If a job fails with NoClassDefFoundError about software/amazon/awssdk: add the AWS SDK v2 bundle jar whose version is named in the Hadoop POM (aws-java-sdk-v2.version).

Does: creates the PostgreSQL role lake_writer, adds the Trino catalog serving (PostgreSQL), restarts Trino and builds a demo chain lake.demo.t > serving.api.demo_t > PostgREST /demo_t. Purpose: proves Trino to PostgreSQL to REST works. Part of the older publishing path.

Does: installs NiFi NIFI_VERSION (SHA-512 checked) to /opt/nifi-<version>, symlink /opt/nifi, keeps heavy repositories on /data/nifi, sets the heap NIFI_HEAP and the single-user login NIFI_USER / NIFI_PASSWORD (12+ characters), unit nifi. Check: grep "NiFi has started" /opt/nifi/logs/nifi-app.log. Open https://localhost:8443/nifi through a tunnel (use the name localhost: NiFi rejects bare IPs).

Does: creates the database keycloak, installs Keycloak KEYCLOAK_VERSION (hash checked) to /opt/keycloak-<version>, writes /etc/lake/keycloak/keycloak.conf and keycloak.env, builds it, imports the realm chdc from /opt/lake/keycloak/realm-chdc.json (only on first start), unit keycloak. Ports: web 8180, health 9180 (both 127.0.0.1). Check: waits for /health/ready, then reads the realm’s OIDC discovery document.

Does: creates the role ingest_app and database ingest if missing (an existing password is left alone), then applies every /opt/lake/sql/0*.sql in name order, and lists the tables. Needs: INGEST_DB_PASSWORD; the SQL copied by deploy.sh. See SQL migrations. Re-run it whenever a new migration is added; the migrations are all “if not exists”.

Does: checks the S3A block exists, installs python3-psycopg2, disables the old FastAPI services if present, copies worker.py, process_csv.py and remove_file.py to /opt/lake-worker, writes /etc/lake/worker/worker.env, unit lake-worker. Needs: 07, 07.5, 14. See Worker.

Does: stops and removes the old FastAPI intake and its worker, after saving a backup to /opt/lake/retired/old-intake-<date>.tar.gz (root only; it holds old secrets). Databases are untouched. Not part of install-all.sh. Needed only on a server that had the old intake.

Does: copies lakestatus.py to /usr/local/bin/lakestatus. See lakestatus.

Does: installs JupyterHub for analysts: npm and the pinned configurable-http-proxy under /opt/jupyterhub/chp; two Python environments (/opt/jupyterhub/venv for the hub, /opt/lake-notebook/venv for every notebook); the config in /etc/lake/jupyterhub/; the Keycloak client chdc-notebook (created or updated each run); unit jupyterhub. Ports (127.0.0.1): 8000 (open this), 8001 proxy API, 8091 hub API. Needs: step 13 running, JUPYTERHUB_CLIENT_SECRET, KEYCLOAK_ADMIN_PASSWORD, CHP_VERSION. Careful choices: installs with --no-install-recommends and NEEDRESTART_MODE=l so it does not pull in about 1,100 packages or restart other services. A re-run restarts only the hub; running notebooks keep going. It warns if cluster.env is readable by every user. See JupyterHub.

Does: sets cluster.env (and copies next to it) to mode 640, owner = the admin user, group lake, then proves the owner and lake can read it and an unprivileged user cannot. Why: notebooks run as other Linux users on the same box; with the usual 664 they could read every password. Safe because: no service reads cluster.env while running; each has its own file in /etc/lake/<service>/. Only the admin runs the install scripts and lakestatus.