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.
install-all.sh
Section titled “install-all.sh”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.
00 · 00-make-user.sh
Section titled “00 · 00-make-user.sh”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.
01 · 01-install-seaweed.sh
Section titled “01 · 01-install-seaweed.sh”Does: downloads the SeaweedFS release SEAWEED_VERSION, checks SEAWEED_SHA256, installs weed to /usr/local/bin.
Check: it prints weed version.
02 · 02-install-postgres.sh
Section titled “02 · 02-install-postgres.sh”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.
03 · 03-install-postgrest.sh
Section titled “03 · 03-install-postgrest.sh”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).
03.5 · 03.5-make-seaweed-folder.sh
Section titled “03.5 · 03.5-make-seaweed-folder.sh”Does: creates /data/seaweed owned by lake: the data directory for object storage.
04 · 04-install-seaweed-service.sh
Section titled “04 · 04-install-seaweed-service.sh”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.
05 · 05-install-nessie.sh
Section titled “05 · 05-install-nessie.sh”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.
05.5 · 05.5-fix-nessie-config_skip.sh
Section titled “05.5 · 05.5-fix-nessie-config_skip.sh”Obsolete. Step 05 now writes that configuration itself. Kept for history; not run.
06 · 06-install-trino.sh
Section titled “06 · 06-install-trino.sh”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.
07 · 07-install-spark.sh
Section titled “07 · 07-install-spark.sh”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.
07.5 · 07.5-spark-s3a.sh
Section titled “07.5 · 07.5-spark-s3a.sh”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).
08 · 08-serving-link.sh
Section titled “08 · 08-serving-link.sh”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.
09 · 09-install-nifi.sh
Section titled “09 · 09-install-nifi.sh”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).
13 · 13-install-keycloak.sh
Section titled “13 · 13-install-keycloak.sh”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.
14 · 14-install-registry.sh
Section titled “14 · 14-install-registry.sh”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”.
15 · 15-install-worker.sh
Section titled “15 · 15-install-worker.sh”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.
16 · 16-retire-old-intake.sh (one-off)
Section titled “16 · 16-retire-old-intake.sh (one-off)”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.
17 · 17-install-lakestatus.sh
Section titled “17 · 17-install-lakestatus.sh”Does: copies lakestatus.py to /usr/local/bin/lakestatus. See lakestatus.
18 · 18-install-jupyterhub.sh
Section titled “18 · 18-install-jupyterhub.sh”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.
19 · 19-protect-cluster-env.sh
Section titled “19 · 19-protect-cluster-env.sh”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.