Tower Services¶
This page is for anyone who wants to share an app on the Tower Services cluster — students, researchers, and other teams. You do not need to know Docker, Airflow, or CoRE Stack already.
Tower Services is a shared cluster. You package your app as a Docker service; if it is accepted, it runs next to the others, uses the shared job runner when you opt in, and writes results where people can download them.
It is not only the campus demo websites. Drone, CEM, and DIY LULC are examples already on the cluster. Your app can be next.
Live URLs of what is running today: Deployed Architecture. To deploy yours: Add your own service.
What you can do today¶
| If you want to… | Do this |
|---|---|
| Try an app that is already deployed | Open it from the table below. No install. |
| Download a finished result | Open FileBrowser. Each app writes into its own folder (data/drone/, data/diy-lulc/, …). |
| Deploy your own app on the cluster | Read Why we use Airflow and How a job runs, then Add your own service. |
| App | What it does | Open |
|---|---|---|
| Drone | Finds tree crowns on a drone orthomosaic | act4dws5/drone |
| CEM (Bioacoustic) | Continuous Ecological Monitoring — spots, species, network | act4dws5/bio-master |
| DIY LULC | 10 m land-use / land-cover; grow classes from example polygons | act4dws5/diy-lulc |
| FileBrowser | Browse and download job output | act4dws5/file |
You will also see Airflow at /airflow. That is the shared job runner. The apps talk to it for you. You do not open Airflow to use Drone or DIY LULC.
Why we use Airflow¶
The apps on this host look like websites, but the work behind Run is not a quick page load. Detecting tree crowns, growing a land-cover map, or processing a recording can take minutes to hours, use a GPU or a lot of RAM, and often has several steps (prepare input → run a model → write a STAC result).
If each app did that work inside its own web container:
- A second person clicking Run would compete with the first for the same CPU/GPU.
- Closing the tab or restarting the app could kill the job.
- Every team would invent its own retries, logs, and “is it done yet?” page.
- You could not see one shared picture of what is running on the cluster.
So the cluster uses one shared Airflow (with STACD to describe the steps in YAML):
| Need | What Airflow + STACD give you |
|---|---|
| Long jobs | The website starts the job and polls. The worker keeps running if you close the tab. |
| Many apps, one cluster | Drone, DIY LULC, and your app queue on the same runner instead of each holding a private compute box. |
| Several steps | A DAG is a graph of tasks (fetch → compute → register). Airflow runs them in order and can retry a failed step. |
| “What failed?” | One Graph / Grid and task logs for every app, instead of grepping a different container each time. |
| Thin app images | The app Docker stays a UI + API. Heavy work is an executor Airflow starts. |
| Lineage | STACD records which algorithm produced which file, so a result is not a bare path on disk. |
That is why the cluster is Airflow-based for compute. The browser still only talks to your app’s path (for example /drone or /diy-lulc). Airflow is the back office.
You can still turn Airflow off on a laptop: leave AIRFLOW_API_BASE empty and the same app runs the job inside its container. That is for local try-out, not for the shared cluster.
How a job runs¶
Think of three people:
- You — the browser. You only talk to the app’s web page.
- The app — a Docker container. It shows the UI, starts the job, and waits until the job finishes.
- The job runner (Airflow + STACD) — optional. If the app is configured to use it, the app sends the job there and checks status until the run is
successorfailed.
When the run succeeds, the app writes files under data/. FileBrowser is just a website over that folder.
flowchart LR
You[You in the browser] --> App[The app]
App -->|job runner is configured| Runner[Airflow job runner]
App -->|job runner is off| Local[Work inside the app]
Runner -->|success| Data["data/ folder"]
Local -->|success| Data
Data --> FB[FileBrowser]
You -.->|download files| FB
sequenceDiagram
participant You
participant App as The app
participant Job as Job runner or the app itself
participant Data as data/ folder
participant FB as FileBrowser
You->>App: open the page and start a job
App->>Job: start work
loop wait until success or failed
App->>Job: is it done?
end
Job-->>App: success
App->>Data: write the result files
You->>FB: download from data/
The browser never talks to Airflow. Only the app does. That is why you use /drone or /diy-lulc, not /airflow, to start work.
Where the work actually runs¶
One setting on the app decides this: AIRFLOW_API_BASE.
AIRFLOW_API_BASE |
What it means for you |
|---|---|
Filled in (for example http://airflow:8080/api/v1) |
The app sends the job to the shared Airflow machine and waits. |
| Empty | The app does the work inside its own container. No Airflow. |
You do not invent a second switch such as COMPUTE_MODE. Empty vs set is the whole rule.
Folders on the machine¶
If you later deploy an app here, the host keeps three folders and mounts them into the container (the container sees them as local paths). Code is not baked into the Docker image.
| Host folder | Path inside the container | What lives there |
|---|---|---|
code/ |
/app |
The git checkout. Update with git pull, then restart the container. |
models/ |
/app/models |
Trained weights (.pt, .onnx, and similar). |
data/ |
/app/data |
Inputs, caches, every job output, and logs under data/logs/<app-name>/. FileBrowser shows this tree. |
The Docker image only has system libraries and Python/Node packages (a deps-only image). Rebuild the image when requirements change. Do not rebuild it just to change application code.
Words you will see¶
These are the names used on this host. Use the same words in a README or GitHub issue so others can follow.
| Word | Plain meaning |
|---|---|
| Tower Services | The shared cluster where anyone can deploy an app (not only the live campus demos). |
| Service | One app (web UI + its Docker container). |
| Frontend Docker | The container your browser talks to. It starts jobs, waits, and writes data/ when the job succeeds. |
| Airflow–STACD Docker | The shared job runner. Used only when AIRFLOW_API_BASE is set. |
AIRFLOW_API_BASE |
Address of Airflow’s REST API. Set = use the job runner. Empty = run inside the app. |
AIRFLOW_DAG_ID |
Name of the workflow this app starts on Airflow. |
CORESTACK_API_BASE |
Address the Airflow worker uses to call this app back. Must be a LAN or Docker name — not localhost from the worker’s point of view. |
| Same-origin proxy | Nginx sends /drone, /diy-lulc, … to the right container. Your browser only talks to that website. |
| FileBrowser | Website over data/. Download results here. |
| STACD | Tool that turns three YAML files into an Airflow workflow (DAG). You do not write Airflow Python by hand for cluster apps. |
| STAC Item | The required job result: a STAC 1.x GeoJSON Feature that describes the output (see the runbook §9). |
| Deps-only image | Image has dependencies and a start command only — no source, models, or outputs. |
| GHCR or Docker Hub | Where the image is pushed. Either registry is fine. |
LOG_LEVEL |
debug, info, or error. Logs on disk: data/logs/<application_name>/. |
| Central Postgres | One shared database for the cluster. Connect with DATABASE_URL. Do not add a private Postgres/SQLite per app on the cluster. |
outputs.yaml |
Lists each tree under data/ and whether it is public, private_persistent, or delete. |
| Host data service | Cluster process that reads outputs.yaml and publishes, keeps, or deletes those trees. |
Do not use AIRFLOW_BASE_API_URL, AIRFLOW_BASE_URL (unless you only mean the Airflow web page), COMPUTE_MODE, /data as the data mount, or /models as the models mount. Older notes used those names; they are not the contract on this host.
Add your own service¶
If you want your app on this cluster, package it the same way as the apps already running. You do not need the full runbook to understand the idea. You do need it when you ask for a deploy.
- Your app has a browser UI.
- When someone starts a job, your container starts the work (on Airflow if
AIRFLOW_API_BASEis set, otherwise locally). - Your container waits until the job is
successorfailed. - On success it writes files under
data/<your-app>/and returns a STAC Item (not only a file path). - People download from FileBrowser.
Short how-to (env, three mounts, STACD YAML, STAC, copy-this LULC): Cluster Docker Services.
Acceptance list to paste into a GitHub issue: Cluster Service Checklist. Tick a row only when the Acceptance line is true.
The runbook is short. Read it top to bottom (§1 env → mounts → image → STACD → trigger → STAC → front page). Copy-this LULC is at the end.
Open Cluster Docker Services Open Cluster Service Checklist
Keep install steps, image name, and a pinned tag in your repo README and VERSION. This page does not replace that.
Checklist mapped to the runbook¶
| Checklist item | Same idea as above |
|---|---|
| 1. Mounts | code/, models/, data/ → /app, /app/models, /app/data — runbook §2 |
| 2. Airflow vs local | AIRFLOW_API_BASE set / empty — runbook §8 |
| 3. Registry | GHCR or Docker Hub, deps-only image — runbook §5 |
| 4–10 | Google SSO, LOG_LEVEL, one container, frontend API base, a diagram of your app, central Postgres, outputs.yaml |
| 11. Front page + demo video | Landing page like Drone; one video (manual + tutorial) on the front page; video reviewed and approved — runbook §11 |
Docker pull on IIT Delhi campus¶
Campus machines often cannot reach GitHub Container Registry or Docker Hub without the Docker daemon proxy. Do not put registry-1.docker.io or auth.docker.io in NO_PROXY.
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf <<'EOF'
[Service]
Environment="HTTP_PROXY=http://proxy21.iitd.ac.in:3128"
Environment="HTTPS_PROXY=http://proxy21.iitd.ac.in:3128"
Environment="NO_PROXY=localhost,127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
Replace the proxy host with the one for your IITD account category. Same steps: Cluster Docker Services — IIT Delhi proxy.
Shared pieces on the host¶
| Piece | Role |
|---|---|
| Airflow + STACD | Shared job runner — STACD Framework (dev) |
| FileBrowser | Download UI over data/ |
| Nginx | Public entry: /drone, /diy-lulc, /bio-master, /file, /airflow |
Install commands stay in each service repository. The live path map is on Deployed Architecture.