Track, trend, and elevate your grow β all in one place.
π Official Site Β· π Report a Bug Β· π‘ Request a Feature
I built Isley because the tool I wanted didn't exist. Every existing option was either a phone app with a bad UX, a cloud service I didn't trust, or a spreadsheet held together with duct tape. I wanted one self-hosted solution to replace all three:
| Before Isley | With Isley |
|---|---|
| π‘οΈ Vendor apps for sensor graphs | Unified environmental dashboard |
| π Spreadsheets for seeds & harvests | Structured grow journal with charts |
| ποΈ Notepads for feeding & watering history | Timestamped activity logs per plant |
Isley doesn't try to revolutionize your grow β it centralizes your tools so you can focus on what matters.
| Feature | Description | |
|---|---|---|
| π | Grow Logs | Track plant growth, watering, and feeding with custom activity types |
| π‘οΈ | Environmental Monitoring | Real-time sensor data from AC Infinity and EcoWitt, plus custom HTTP ingest |
| πΈ | Image Uploads | Attach photos with captions; add text overlays and watermarks |
| π· | Webcam Integration | Capture periodic snapshots from camera streams via FFmpeg |
| π± | Seed Inventory | Manage strains, breeders, and seed stock with Indica/Sativa and autoflower tracking |
| π | Harvest Tracking | Record harvest dates, yields, and full cycle times |
| π¬οΈ | Smoke Reports | Rate each harvest after curing with configurable tasting criteria, and see a strain's average across harvests |
| π | Graphs and Charts | Visualize sensor data over time with configurable retention windows |
| βοΈ | Customizable Settings | Define custom zones, activities, metrics, and camera streams |
| π | Internationalization | Available in English, German, Spanish, and French |
| π | Guest Mode | Optional read-only access for unauthenticated visitors |
| πΎ | Backup & Restore | Cross-database portable backups with optional image bundling and sensor data filtering |
| π± | Mobile-Friendly | Responsive layout for desktop and mobile |
- π Alerts and Notifications β Set custom thresholds and get notified when conditions go out of range.
Isley runs in Docker and is up in minutes. PostgreSQL is recommended for production; SQLite works great for local testing.
Prerequisites: Docker and docker-compose
# docker-compose.postgres.yml
version: '3.8'
services:
isley:
image: dwot/isley:latest
ports:
- "8080:8080"
environment:
- ISLEY_PORT=8080
- ISLEY_DB_DRIVER=postgres
- ISLEY_DB_HOST=postgres
- ISLEY_DB_PORT=5432
- ISLEY_DB_USER=isley
- ISLEY_DB_PASSWORD=supersecret
- ISLEY_DB_NAME=isleydb
- ISLEY_SESSION_SECRET=change-me-to-a-long-random-string
depends_on:
- postgres
volumes:
- isley-uploads:/app/uploads
restart: unless-stopped
postgres:
image: postgres:16.8-alpine
environment:
- POSTGRES_DB=isleydb
- POSTGRES_USER=isley
- POSTGRES_PASSWORD=supersecret
volumes:
- postgres-data:/var/lib/postgresql/data
restart: unless-stopped
volumes:
postgres-data:
isley-uploads:docker-compose -f docker-compose.postgres.yml up -dThen open http://localhost:8080 β default login is admin / isley. You'll be prompted to change your password on first login.
# docker-compose.sqlite.yml
version: '3.8'
services:
isley:
image: dwot/isley:latest
ports:
- "8080:8080"
environment:
- ISLEY_PORT=8080
- ISLEY_DB_DRIVER=sqlite
volumes:
- isley-db:/app/data
- isley-uploads:/app/uploads
restart: unless-stopped
volumes:
isley-db:
isley-uploads:docker-compose -f docker-compose.sqlite.yml up -dNote: SQLite is not recommended for production due to write contention under concurrent load.
Already running Isley with SQLite? Isley handles the migration automatically.
Use docker-compose.migration.yml β it mounts both your existing SQLite volume and the new PostgreSQL instance. On startup, if Isley finds existing SQLite data and an empty PostgreSQL instance, it will import everything automatically.
# docker-compose.migration.yml
version: '3.8'
services:
isley:
image: dwot/isley:latest
ports:
- "8080:8080"
environment:
- ISLEY_PORT=8080
- ISLEY_DB_DRIVER=postgres
- ISLEY_DB_HOST=postgres
- ISLEY_DB_PORT=5432
- ISLEY_DB_USER=isley
- ISLEY_DB_PASSWORD=supersecret
- ISLEY_DB_NAME=isleydb
depends_on:
- postgres
volumes:
- isley-db:/app/data
- isley-uploads:/app/uploads
restart: unless-stopped
postgres:
image: postgres:16.8-alpine
environment:
- POSTGRES_DB=isleydb
- POSTGRES_USER=isley
- POSTGRES_PASSWORD=supersecret
volumes:
- postgres-data:/var/lib/postgresql/data
restart: unless-stopped
volumes:
isley-db:
postgres-data:
isley-uploads:Tip: Back up your
isley-dbvolume before running migration, just in case.
After migration completes, switch back to docker-compose.postgres.yml for your regular deployment.
Older shipped compose files included a hardcoded
healthcheck: wget http://localhost:8080/health block on the isley service.
A Compose-level healthcheck fully overrides the Dockerfile one, so it
always probed port 8080 and ignored your ISLEY_PORT β a container running
on a custom port was reported unhealthy ("connection refused").
As of v0.2.0 the healthcheck lives only in the Dockerfile and honors
${ISLEY_PORT:-8080}. To pick this up on an existing deployment:
- Pull the latest
docker-compose.*.ymlor delete thehealthcheck:block from theisleyservice in your existing file. - Recreate the container so the change takes effect:
docker compose up -d --force-recreate
- If Isley itself listens on a custom port, set
ISLEY_PORTin the container environment (not just a host port remap like"420:8080").
Most settings are managed through the Settings panel in the app β enable integrations, set device IPs, scan for sensors, and more.
For environment-level configuration, the full reference is below:
| Variable | Default | Description |
|---|---|---|
ISLEY_PORT |
8080 |
Port Isley listens on |
ISLEY_SESSION_SECRET |
(random) | Session encryption key (must be β₯32 bytes) β set this in production |
ISLEY_SECURE_COOKIES |
false |
Set to true to mark session cookies as Secure (cookies only sent over HTTPS). Enable when Isley is fronted by a TLS reverse proxy. |
ISLEY_HSTS_MAX_AGE |
0 |
When > 0, emits a Strict-Transport-Security: max-age=<seconds> header on every response. Only enable when Isley is reachable solely over HTTPS β sending HSTS over plain HTTP poisons clients that previously connected over TLS. A common production value is 31536000 (one year). |
ISLEY_HSTS_INCLUDE_SUBDOMAINS |
false |
When ISLEY_HSTS_MAX_AGE > 0, append ; includeSubDomains to the HSTS header. |
ISLEY_HSTS_PRELOAD |
false |
When ISLEY_HSTS_INCLUDE_SUBDOMAINS=true, append ; preload to the HSTS header. Only opt in if you intend to submit the host to https://hstspreload.org. |
GIN_MODE |
release |
Set to debug for verbose request logging and error details during development |
| Variable | Default | Description |
|---|---|---|
ISLEY_DB_DRIVER |
sqlite |
Database backend: sqlite or postgres |
ISLEY_DB_FILE |
data/isley.db |
SQLite database path |
ISLEY_DB_HOST |
β | PostgreSQL host |
ISLEY_DB_PORT |
5432 |
PostgreSQL port |
ISLEY_DB_USER |
β | PostgreSQL username |
ISLEY_DB_PASSWORD |
β | PostgreSQL password |
ISLEY_DB_NAME |
β | PostgreSQL database name |
ISLEY_DB_SSLMODE |
disable |
PostgreSQL SSL mode β set to require (or verify-full) when connecting to a TLS-enforcing Postgres host |
Isley exposes an HTTP API for pushing sensor data from custom devices, IoT hardware, or home automation systems.
- Log in as an admin and go to Settings β API Settings.
- Give the key a name (e.g. the device it's for), click Add Key, and copy it somewhere safe β it's shown only once and can't be retrieved later.
- Include the key as an
X-API-KEYheader on all API requests.
You can keep several keys at once β add one per device, see when each was last used, and regenerate or revoke any of them individually so rotating one device's key doesn't disturb the others.
Each key has an access level and, separately, permission to push sensor readings. Give a key only what it needs; you can change a key's access later without changing the key itself.
| Access | Can |
|---|---|
| Read only | read data, e.g. GET /api/overlay |
| Read and write | read, and record plants, activities and measurements (for the upcoming /api/v1) |
| Admin | everything, including settings, deletes, backups, and the endpoints the web UI itself uses |
| Sensor ingest | POST /api/sensors/ingest (can be combined with any level; admin includes it) |
New keys default to Read only. Keys created before scoped keys existed are
Admin, so existing integrations keep working. A request a key isn't allowed to
make gets 403 with "code": "insufficient_scope" and the scope it needed in
"required".
A versioned JSON API at /api/v1. Every Isley instance serves a guide at
/api/v1/docs (keys, conventions, times, errors, paging, safe retries and
worked examples) and the full OpenAPI 3.1 reference at /api/v1/openapi.yaml,
which any OpenAPI tool can load. Settings β API links to the guide.
curl -H "Authorization: Bearer $ISLEY_KEY" https://isley.example/api/v1/instanceGET /api/v1/instance reports the version, the instance time zone (every
timestamp in the API carries its offset), enabled features, and what the
calling key may do. Errors are {"error": {"code": "...", "message": "..."}}
with a stable code.
Available so far:
| Endpoint | Key |
|---|---|
GET /instance |
read |
GET /statuses, GET /metrics, GET /activity-types |
read |
GET /zones, GET /zones/{id} (with sensors and latest readings) |
read |
POST /zones, PATCH /zones/{id}, DELETE /zones/{id} (empty zones only) |
admin |
GET /plants (filter by phase, status_id, zone_id, strain_id), GET /plants/{id} |
read |
GET /plants/{id}/status-history |
read |
POST /plants, PATCH /plants/{id}, PUT /plants/{id}/sensors, POST /plants/{id}/status, PATCH/DELETE /plants/{id}/status-history/{entry_id} |
write |
GET /activities (log: plant_id, zone_id, activity_type_id, from, to, q), GET /activities/{id}, GET /plants/{id}/activities, GET /plants/{id}/measurements |
read |
POST /plants/{id}/activities, POST /activities/bulk, PATCH/DELETE /activities/{id}, POST /plants/{id}/measurements, PATCH/DELETE /measurements/{id} |
write |
GET /plants/{id}/images, GET /images/{id} |
read |
POST /plants/{id}/images (multipart, one image per request), DELETE /images/{id} |
write |
GET /streams (filter by zone_id), GET /streams/{id} |
read |
GET /smoke-criteria, GET /plants/{id}/smoke-reports, GET /smoke-reports/{id}, GET /strains/{id}/smoke-summary |
read |
POST /plants/{id}/smoke-reports, PATCH/DELETE /smoke-reports/{id} |
write |
GET /sensors (filter by zone_id, source), GET /sensors/{id} (with the latest reading), GET /sensors/{id}/data (from, to, bucket = raw or 5m/1h/1d...) |
read |
GET /strains (filter by breeder_id, in_stock, q), GET /strains/{id}, GET /strains/{id}/lineage, GET /strains/{id}/descendants, GET /breeders, GET /breeders/{id} |
read |
POST /strains, PATCH /strains/{id}, POST /strains/{id}/seed-count, PUT /strains/{id}/lineage, POST /breeders, PATCH /breeders/{id} |
write |
DELETE /plants/{id}, DELETE /strains/{id} (no plants), DELETE /breeders/{id} (no strains), PATCH/DELETE /sensors/{id}, POST /streams, PATCH/DELETE /streams/{id} |
admin |
Any POST can send an Idempotency-Key header so a retry doesn't record the
same thing twice. Every write is logged on the server with the key's name, so
give each script or device its own key.
# Log a watering on plant 12 (activity type 1 is Water; see GET /activity-types)
curl -H "Authorization: Bearer $ISLEY_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"activity_type_id": 1, "note": "250 ml"}' \
https://isley.example/api/v1/plants/12/activities
# Temperature overnight, hour by hour
curl -H "Authorization: Bearer $ISLEY_KEY" \
"https://isley.example/api/v1/sensors/2/data?from=2026-11-10T20:00&to=2026-11-11T08:00&bucket=1h"Isley can serve AI assistants such as Claude over the
Model Context Protocol at /mcp. It is off
until you turn on Enable MCP server in Settings β API. An assistant connects
with an API key as a bearer token and gets exactly that key's powers: a
Read only key can look things up ("how did the tent do overnight?"), a
Read and write key can also start plants, log waterings and feedings, record
measurements and change statuses. There are no delete tools.
Claude Code:
claude mcp add --transport http isley https://isley.example/mcp \
--header "Authorization: Bearer $ISLEY_KEY"Claude Desktop, through the mcp-remote bridge, in claude_desktop_config.json:
{
"mcpServers": {
"isley": {
"command": "npx",
"args": ["mcp-remote", "https://isley.example/mcp", "--header", "Authorization:${ISLEY_AUTH}"],
"env": { "ISLEY_AUTH": "Bearer <your key>" }
}
}
}| Tool | Key | Does |
|---|---|---|
get_overview |
read | growing plants (age, status, zone, days since watered and fed) and each zone's latest readings |
list_plants, get_plant |
read | plants by phase, status, zone or strain; one plant with its history, sensors and recent activity |
find_strains, get_strain |
read | strains by name, breeder or stock; one strain with its lineage |
list_zones, get_sensor_history |
read | zones and sensors; min/avg/max over a time range, bucketed, with on-time for fans and lights |
list_reference_data |
read | the status, activity and metric names the write tools take |
create_plant, update_plant, set_plant_status |
write | start a plant, change it, move it to a new stage (with harvest weight) |
log_activity, record_measurement |
write | log watering, feeding or a note on one or more plants; record a measurement |
adjust_seed_count, link_plant_sensors |
write | add or take seeds; set a plant's own sensors |
Tools take names where people use names ("Water", "Flower") and times in the instance zone unless they carry an offset. Every write is logged with the key's name.
That covers the plan's resources; an MCP server comes next.
The endpoints below predate API v1 and stay supported for the integrations that
use them: push readings with /api/sensors/ingest, and read with the overlay and
sensor data endpoints. New integrations should use API v1, which
covers the reads with documented, stable responses. (The web UI also has its own
internal endpoints; they aren't an API and can change between versions.)
POST /api/sensors/ingest β needs a key with Sensor ingest (or Admin).
{
"source": "custom",
"device": "Arduino Sensor",
"type": "temperature",
"value": 25.5,
"name": "Temperature Sensor 1",
"new_zone": "Tent 1",
"unit": "Β°C"
}source, device, type and value are required; value may be 0. A sensor is
matched on source + device + type, so keep those stable and readings append to
the same history. name, unit and the zone (zone_id, or new_zone, which finds
or creates a zone by name) are applied when the sensor is first created.
Temperature units can be sent as the symbols Β°C / Β°F or as the words
celsius / fahrenheit (any case), which are stored as the symbols. Any other unit
is stored exactly as sent.
Batches: send a JSON array of up to 60 readings to store several in one request, for example everything a device measured in one cycle:
[
{ "source": "esp32", "device": "node-1", "type": "temperature", "value": 24.7, "unit": "celsius", "new_zone": "Tent 1" },
{ "source": "esp32", "device": "node-1", "type": "humidity", "value": 58.2, "unit": "%", "new_zone": "Tent 1" },
{ "source": "esp32", "device": "node-1", "type": "co2", "value": 612, "unit": "ppm", "new_zone": "Tent 1" }
]A batch is validated as a whole before anything is stored (one bad reading rejects
it with 400 and stores nothing), its readings share one timestamp, and the
response lists sensor_ids in request order. Each reading counts toward the ingest
rate limit of 60 per minute per key; a 429 carries a Retry-After header in seconds.
Use this for: Arduino/ESP32 sensors, Home Assistant, Node-RED, or any off-the-shelf sensor not natively supported by Isley.
GET /api/overlay β Returns a JSON snapshot of all living plants (with linked sensor readings) and grouped sensor data. Designed for live-stream overlays (e.g., OBS browser sources).
Requires the X-API-KEY header, with a key that has at least Read only access.
Response structure:
{
"plants": [
{
"id": 1,
"name": "White Widow #3",
"strain_name": "White Widow",
"zone_name": "Tent A",
"status": "Flower",
"current_day": 42,
"linked_sensors": [
{
"name": "Tent A Temp",
"value": 24.5,
"unit": "Β°C",
"trend": "up",
"source": "ACI",
"type": "temperature"
}
]
}
],
"sensors": { }
}The sensors object groups all sensors by source, zone, and type with their latest readings.
GET /sensorData β Returns historical sensor readings for charting. Requires authentication unless guest mode is enabled.
Query parameters:
| Parameter | Required | Description |
|---|---|---|
sensor |
Yes | Sensor ID |
minutes |
One of minutes or start/end |
Number of minutes of history to return |
start |
One of minutes or start/end |
Start date (YYYY-MM-DD or RFC 3339) |
end |
One of minutes or start/end |
End date (YYYY-MM-DD or RFC 3339) |
Example:
GET /sensorData?sensor=5&minutes=1440
Returns an array of { "id", "sensor_id", "sensor_name", "value", "create_dt" } objects.
Isley supports two sensor platforms out of the box, plus a generic HTTP ingest API for custom hardware.
AC Infinity controllers (UIS series) expose temperature, humidity, and per-port speed data. Isley communicates with the AC Infinity cloud API β your controller must be online and linked to an AC Infinity account.
Setup:
- Go to Settings and enable AC Infinity Sensor Monitoring.
- Click Retrieve Token and enter your AC Infinity account email and password. Isley authenticates once and stores the API token locally β your password is not saved.
- After the token is set, go to Sensors and click Scan and Add AC Infinity Sensors. Isley will discover all controllers and their sensor channels.
- Assign each sensor to a Zone and toggle visibility for sensors you want to display.
Sensor data is polled at the interval configured in Settings β Polling Interval (default 60 seconds). Data appears on the dashboard and can be linked to individual plants for per-plant environmental monitoring.
Note: AC Infinity's API limits password length to 25 characters. If your password is longer, only the first 25 characters are used during authentication.
EcoWitt weather stations and soil sensors expose data over a local HTTP API on your network. No cloud account is required β Isley communicates directly with the EcoWitt hub.
Setup:
- Ensure your EcoWitt hub (e.g., GW1000, GW1100, GW2000) is on the same network as your Isley instance.
- Go to Settings and enable EcoWitt Sensor Monitoring.
- Go to Sensors and click Scan and Add EcoWitt Sensors. Enter the IP address of your EcoWitt hub (e.g.,
192.168.1.50) and click Scan. - Isley will discover soil moisture and indoor temperature/humidity channels. Assign zones and set visibility as needed.
EcoWitt data is polled at the same Polling Interval as AC Infinity. Multiple EcoWitt hubs are supported β scan each one individually.
For hardware not natively supported (Arduino, ESP32, Home Assistant, etc.), use the HTTP ingest endpoint documented in the API & Integrations section above. Any device that can make an HTTP POST can push sensor data into Isley.
Isley stores data in two Docker volumes that should be backed up regularly.
| Volume | Contents | Path inside container |
|---|---|---|
postgres-data (or isley-db for SQLite) |
Database β all plants, sensors, settings, and history | /var/lib/postgresql/data (Postgres) or /app/data (SQLite) |
isley-uploads |
Plant photos, stream snapshots, and logo images | /app/uploads |
PostgreSQL:
# Dump the database to a SQL file
docker exec isley-postgres-1 pg_dump -U isley isleydb > isley_backup_$(date +%Y%m%d).sql
# Back up the uploads volume
docker cp isley-isley-1:/app/uploads ./isley_uploads_backupSQLite:
# Copy the database file (stop the container first to avoid corruption)
docker compose -f docker-compose.sqlite.yml stop
docker cp isley-isley-1:/app/data ./isley_data_backup
docker cp isley-isley-1:/app/uploads ./isley_uploads_backup
docker compose -f docker-compose.sqlite.yml startPostgreSQL:
# Restore the database from a SQL dump
cat isley_backup_20260315.sql | docker exec -i isley-postgres-1 psql -U isley isleydb
# Restore uploads
docker cp ./isley_uploads_backup/. isley-isley-1:/app/uploadsSQLite:
docker compose -f docker-compose.sqlite.yml stop
docker cp ./isley_data_backup/. isley-isley-1:/app/data
docker cp ./isley_uploads_backup/. isley-isley-1:/app/uploads
docker compose -f docker-compose.sqlite.yml startTip: Automate backups with a cron job. For PostgreSQL,
pg_dumpcan run while the database is online with no downtime.
Isley includes a built-in backup system accessible from the Settings > Backup tab. It produces portable .zip archives that work across database backends β you can back up a SQLite instance and restore it onto PostgreSQL, or vice versa.
Navigate to Settings > Backup and choose your options before clicking Create Backup:
| Option | Values | Effect |
|---|---|---|
| Sensor History | All, Last 7/30/90 days, None | Controls how much sensor data is included. Excluding sensor data keeps backups small and fast. |
| Include Images | On / Off | Bundles uploaded plant photos and stream snapshots into the archive. Can significantly increase backup size. |
Backups run asynchronously β you can navigate away and return later. Completed archives appear in the Available Backups table for download or deletion.
A backup archive contains a backup.json file with a full export of all application data (plants, strains, breeders, zones, activities, metrics, sensors, sensor readings, status history, measurements, images metadata, and streams), plus an optional uploads/ directory with image files. The manifest records the Isley version, source database driver, creation timestamp, and the options used.
Upload a .zip archive in the Restore Backup section. The restore process replaces all existing data and runs through several phases: clearing existing tables, inserting data, resetting sequences (PostgreSQL), and extracting image files. A progress indicator shows the current phase and table-level progress throughout.
For SQLite users, a Skip sensor data option is available to dramatically speed up imports when sensor history isn't needed.
Warning: Restoring a backup is destructive β it replaces all data in the current instance. Sensor polling is paused automatically during the restore.
Backups are stored as JSON, not SQL, so they are database-agnostic. A backup created on SQLite can be restored onto PostgreSQL and vice versa. Foreign keys, auto-increment sequences, and driver-specific optimizations are handled automatically during restore.
SQLite users have an additional option under SQLite File Transfer: download or upload the raw .db database file directly. This is the fastest way to clone or migrate a SQLite instance since it bypasses row-by-row import entirely. The uploaded file is validated by checking the SQLite magic header before replacing the active database.
The maximum upload size defaults to 5 GB and applies to both backup archive uploads and SQLite file uploads. It also caps the total bytes extracted from a backup archive to defend against decompression bombs. You can adjust this limit in the Restore Backup section of Settings β the minimum is 100 MB.
- Full backups only β every backup is a complete export; incremental or delta backups are not supported.
- SQLite restore performance β importing large sensor datasets into SQLite is significantly slower than PostgreSQL due to SQLite's single-writer architecture. Use the Skip sensor data toggle or the SQLite File Transfer feature for faster restores.
- Memory usage β backup archives are read into memory during restore. Very large backups (multi-GB with images) will temporarily consume a corresponding amount of RAM.
- No scheduled backups β backups must be triggered manually from the UI or API. For automated backups, use the Docker volume approach described above or call the
POST /settings/backup/createendpoint from a cron job.
Running Isley behind a reverse proxy is recommended for production. This enables HTTPS, custom domain routing, and keeps Isley off a public port.
server {
listen 443 ssl;
server_name isley.example.com;
ssl_certificate /etc/letsencrypt/live/isley.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/isley.example.com/privkey.pem;
client_max_body_size 20M; # allow image uploads
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 80;
server_name isley.example.com;
return 301 https://$host$request_uri;
}Add these labels to the isley service in your compose file:
services:
isley:
# ... existing config ...
labels:
- "traefik.enable=true"
- "traefik.http.routers.isley.rule=Host(`isley.example.com`)"
- "traefik.http.routers.isley.entrypoints=websecure"
- "traefik.http.routers.isley.tls.certresolver=letsencrypt"
- "traefik.http.services.isley.loadbalancer.server.port=8080"Note: Isley ships with trusted proxy configuration for private RFC-1918 ranges, so
X-Forwarded-Forheaders from your reverse proxy will be used correctly for rate limiting and logging.
- Use Docker with PostgreSQL behind a reverse proxy (Nginx, Traefik) for TLS termination and clean URL routing.
- Back up these volumes on a regular schedule β see Backup & Restore above.
- Set
ISLEY_SESSION_SECRETto keep sessions valid across container restarts. - Configure a sensor data retention period in Settings to prevent unbounded database growth.
- Plant photos under
/uploadsare served without a login, so guest mode and shared links work. Their file names are random and unguessable, but anyone you give a photo's URL to can open it.
- Isley is in active development π§ β breaking changes may occasionally occur between releases.
- Found a bug or have a feature request? Open an issue β contributions welcome.
π For screenshots, feature highlights, and the latest news: dwot.github.io/isley
