diff --git a/.claude/commands/implement-container-deployment.md b/.claude/commands/implement-container-deployment.md index dd9009cd..f6d4ec33 100644 --- a/.claude/commands/implement-container-deployment.md +++ b/.claude/commands/implement-container-deployment.md @@ -13,7 +13,7 @@ Follow closely the architectural patterns described in the `docker/guidelines.md ### Part 1 - Create Docker Compose file - Read the `docker/guidelines.md` file for the architectural patterns you must follow. -- Read the INSTALL_INSTRUCTIONS_FILE file and use its content to create the compose file in the required structure. +- Read the INSTALL_INSTRUCTIONS_FILE file and use its content to create the compose file in the required structure. Abort if the file is not specified or does not exist. - Based on these patterns and the found examples on the installation instructions page, create the Docker Compose file and save it as `docker//.yaml`. - Ensure the compose file contains a brief description of the project and links to the homepage, GitHub page, and any Docker or Docker Compose setup example (if available). - If the installation guide suggests enhancements (e.g., using an optional external database instead of a built-in one, or enabling SSO), add TODOs at the top of the compose file. diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4453368d..2d14c6b1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,7 +10,11 @@ repos: - id: trailing-whitespace args: ["--markdown-linebreak-ext=md,yaml"] - id: end-of-file-fixer - exclude: "docker/security/authelia/" + exclude: | + (?x)^( + docker/security/authelia/| + docker/automation/mosquitto/ + ) - id: mixed-line-ending - id: fix-byte-order-marker - id: check-merge-conflict diff --git a/AGENTS.md b/AGENTS.md index da908106..85748707 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -48,7 +48,7 @@ task docker:pull-update task docker:stop # Create/update Docker example environment configuration files -task docker:create-example-env +task docker:update-example-env ``` ### Service Management diff --git a/README.md b/README.md index 2c381e1b..985d74a7 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ Some development workflows: Code quality scanning tools are set up via [pre-commit](https://pre-commit.com). Perform the checks by running `task lint` (or `pre-commit run --all-files`). -Create/update example `.env` files: `task docker:create-example-env`. +Create/update example `.env` files: `task docker:update-example-env`. ### GitHub automations diff --git a/Taskfile.yaml b/Taskfile.yaml index c7995486..f6123738 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -9,6 +9,7 @@ includes: docker: ./docker/Taskfile.docker.yaml app: ./docker/Taskfile.apps.yaml crowdsec: ./docker/security/Taskfile.crowdsec.yaml + obsidian: ./docker/tools/Taskfile.obsidian.yaml docs: ./docs/web/Taskfile.web.yaml devcontainer: ./.devcontainer/Taskfile.dev.yaml azure-vm: @@ -55,7 +56,7 @@ tasks: build: desc: Run all linting and build tasks cmds: - - task: docker:create-example-env + - task: docker:update-example-env - task: docs-deploy - task: pre-commit-update - task: lint @@ -130,6 +131,20 @@ tasks: - cd config && tar -cjf "../infra-config-backup/infra-config-$(date +%Y-%m-%d_%H-%M-%S).tar.bz2" . - ls -lh ../infra-config-backup/infra-config-*.tar.bz2 | tail -n 5 + # Setup scheduling with command `crontab -e`, add this line: + # 0 1 * * * cd /home/buba/repos/infra && /home/linuxbrew/.linuxbrew/bin/task backup >> config/log/infra-backup.log 2>&1 + backup: + desc: Create backup of the configuration directory and the Obsidian vault + silent: true + cmds: + - echo "=== Backup started at $(date) ===" + - task: backup-config + - task: obsidian:repo-backup + - | + if [ -f "config/sync-backup-to-remote.sh" ]; then + bash "config/sync-backup-to-remote.sh" + fi + get-offline-data: desc: Download data files for offline use (stored both local and on Proxmox) cmds: diff --git a/config-example/docker/myhost/.env b/config-example/docker/myhost/.env index 232a2cee..7d03c7ac 100644 --- a/config-example/docker/myhost/.env +++ b/config-example/docker/myhost/.env @@ -9,6 +9,7 @@ STORAGE_CALIBRE_LIBRARY=${STORAGE_LOCAL_MEDIA}/books STORAGE_CALIBRE_LIBRARY_HUN=${STORAGE_LOCAL_MEDIA}/books-hun STORAGE_WEBDAV=${STORAGE_LOCAL_MEDIA}/webdav STORAGE_KIWIX=${STORAGE_LOCAL_MEDIA}/kiwix +STORAGE_OBSIDIAN=${STORAGE_LOCAL_MEDIA}/obsidian DOCKER_VOLUMES=${STORAGE_LOCAL}/docker-volumes @@ -90,6 +91,23 @@ COUCHDB_PASSWORD="use-some-very-secure-value-here" OBSIDIAN_LIVESYNC_DATABASE_NAME="obsidian" OBSIDIAN_LIVESYNC_PASSPHRASE="use-some-very-secure-value-here" +# Optional Obsidian container authentication +# OBSIDIAN_CUSTOM_USER="user" +# OBSIDIAN_PASSWORD="use-some-very-secure-value-here" +# OBSIDIAN_LC_ALL="en_US.UTF-8" + +### Gitea configuration +# Generate these with: docker run --rm gitea/gitea:1.24.6 gitea generate secret SECRET_KEY +GITEA_SECRET_KEY="use-some-very-secure-value-here" +# Generate these with: docker run --rm gitea/gitea:1.24.6 gitea generate secret INTERNAL_TOKEN +GITEA_INTERNAL_TOKEN="use-some-very-secure-value-here" + +### Microsoft SQL Server configuration +# Password requirements: At least 8 characters, with uppercase, lowercase, numbers, and special characters +MSSQL_SA_PASSWORD="use-some-very-secure-value-here" +# Optional: Custom collation (default: SQL_Latin1_General_CP1_CI_AS) +# MSSQL_COLLATION="SQL_Latin1_General_CP1_CI_AS" + ### Cloud accounts CLOUDFLARE_DNS_API_TOKEN="use-some-very-secure-value-here" diff --git a/config-example/docker/myhost/services.yaml b/config-example/docker/myhost/services.yaml index ae53ea8c..ca7d06f7 100644 --- a/config-example/docker/myhost/services.yaml +++ b/config-example/docker/myhost/services.yaml @@ -1,4 +1,5 @@ # Docker service configuration for myhost +# yaml-language-server: $schema=../services-schema.yaml --- services: - security: @@ -46,8 +47,10 @@ services: state: up - name: n8n state: up - - name: olivetin - state: down + - name: mosquitto + state: up + - name: esphome-device-builder + state: up - media/video: - name: jellyfin @@ -89,6 +92,8 @@ services: state: up - name: couchdb state: up + - name: mssql-server + state: up - backup: - name: kopia-nas @@ -101,6 +106,8 @@ services: - dev: - name: code-server state: up + - name: gitea + state: up - name: gitlab state: down - name: jupyter-notebook @@ -123,6 +130,8 @@ services: state: up - name: searxng state: up + - name: obsidian + state: up - ai: - name: ollama @@ -151,9 +160,9 @@ services: state: down - name: bazarr state: down - # TODO replace with Lazy Librarian - - name: readarr - state: down + # TODO Deprecated - replace with Lazy Librarian - https://lazylibrarian.gitlab.io/ + # - name: readarr + # state: down - name: prowlarr state: down - name: jellyseerr diff --git a/config-example/docker/services-schema.yaml b/config-example/docker/services-schema.yaml new file mode 100644 index 00000000..e777ba01 --- /dev/null +++ b/config-example/docker/services-schema.yaml @@ -0,0 +1,51 @@ +# YAML Schema definition for the Docker service configuration file + +$schema: http://json-schema.org/draft-07/schema# +title: Docker Service Configuration +description: Configuration schema for Docker services in a homelab environment +type: object + +properties: + services: + type: array + description: Categories of Docker services + items: + type: object + additionalProperties: + type: array + description: List of services in a specific category + items: + type: object + required: + - name + - state + properties: + name: + type: string + description: Name of the Docker service + state: + type: string + description: Current state of the service + enum: + - up + - down + note: + type: string + description: Optional note about the service + +examples: + - services: + - security: + - name: crowdsec + state: up + - name: traefik + state: up + - dashboard: + - name: homepage + state: up + - media/video: + - name: jellyfin + state: up + note: Storage on NAS + +additionalProperties: false diff --git a/docker/Taskfile.docker.yaml b/docker/Taskfile.docker.yaml index e8529389..ad1c8c34 100644 --- a/docker/Taskfile.docker.yaml +++ b/docker/Taskfile.docker.yaml @@ -66,17 +66,20 @@ tasks: cmds: - docker run --rm -v /var/run/docker.sock:/var/run/docker.sock containrrr/watchtower --run-once --cleanup --monitor-only - create-example-env: + update-example-env: desc: Create/update Docker example environment configuration files dotenv: ['config/docker/.env'] cmds: - - scripts/create-example-env.py config/docker/.env > config-example/docker/.env - - scripts/create-example-env.py config/docker/${MAIN_NODE}/.env > config-example/docker/myhost/.env + - scripts/update-example-env.py config/docker/.env > config-example/docker/.env + - scripts/update-example-env.py config/docker/${MAIN_NODE}/.env > config-example/docker/myhost/.env - cat config/docker/${MAIN_NODE}/services.yaml | sed "s/${MAIN_NODE}/myhost/g" > config-example/docker/myhost/services.yaml + - cp config/docker/services-schema.yaml config-example/docker/services-schema.yaml sources: - config/docker/**/.env - config/docker/**/services.yaml + - config/docker/services-schema.yaml generates: - config-example/docker/.env - config-example/docker/myhost/.env - config-example/docker/myhost/services.yaml + - config-example/docker/services-schema.yaml diff --git a/docker/automation/esphome-device-builder.yaml b/docker/automation/esphome-device-builder.yaml new file mode 100644 index 00000000..f08c8f9a --- /dev/null +++ b/docker/automation/esphome-device-builder.yaml @@ -0,0 +1,44 @@ +# ESPHome Device Builder is a tool for creating custom firmware for ESP8266/ESP32 microcontrollers +# through simple YAML configuration files and managing them through Home Automation systems like Home Assistant. +# +# 🏠 Home: https://esphome.io +# 📦 Source: https://github.com/esphome/esphome +# 📜 Docs: https://esphome.io/guides/getting_started_command_line/#bonus-esphome-device-builder +# Docker: https://hub.docker.com/r/esphome/esphome +# +# TODO: Consider implementing non-privileged mode with specific device permissions for improved security +# TODO: Set up udev rules for automatic USB device discovery +--- +name: esphome-device-builder +services: + esphome-device-builder: + image: esphome/esphome:2025.9 + container_name: esphome-device-builder + restart: unless-stopped + network_mode: host + environment: + TZ: ${TIMEZONE} + PUID: ${PUID} + PGID: ${PGID} + # kics-scan ignore-block + volumes: + - /etc/localtime:/etc/localtime:ro + # Not needed due to 'network_mode: host' + # ports: + # - "6052:6052" + # Required for USB device access + # kics-scan ignore-line + # privileged: true + # For USB device passthrough (uncomment and adjust as needed): + # devices: + # - /dev/ttyUSB0:/dev/ttyUSB0 + labels: + traefik.enable: true + traefik.http.routers.esphome-device-builder.rule: Host(`esphome.${MYDOMAIN}`) + traefik.http.routers.esphome-device-builder.middlewares: localaccess@file + traefik.http.services.esphome-device-builder.loadbalancer.server.port: 6052 + homepage.group: Automation + homepage.name: ESPHome Builder + homepage.icon: esphome.png + homepage.href: https://esphome.${MYDOMAIN}/ + homepage.description: "Firmware builder for ESP devices" diff --git a/docker/automation/mosquitto.yaml b/docker/automation/mosquitto.yaml new file mode 100644 index 00000000..1a45d4b9 --- /dev/null +++ b/docker/automation/mosquitto.yaml @@ -0,0 +1,45 @@ +# Eclipse Mosquitto is an open-source MQTT broker implementing versions 5.0, 3.1.1, and 3.1 of the MQTT protocol. +# It is lightweight and suitable for devices from single board computers to full servers, providing publish/subscribe +# messaging capabilities with support for TLS, WebSockets, and various authentication methods. +# +# 🏠 Home: https://mosquitto.org/ +# 📦 Source: https://github.com/eclipse/mosquitto +# 📜 Docs: https://hub.docker.com/_/eclipse-mosquitto +# Configure Authentication: https://mosquitto.org/documentation/authentication-methods/ +# +# TODO: Consider enabling TLS/SSL certificates for secure connections on port 8883 +# TODO: Implement authentication with password file and ACL for access control +# TODO: Configure performance tuning for high-throughput environments (max_connections, max_queued_messages) +--- +name: mosquitto +services: + mosquitto: + image: eclipse-mosquitto:2.0.22-openssl + container_name: mosquitto + restart: unless-stopped + environment: + TZ: ${TIMEZONE} + ports: + - "1883:1883" # Standard MQTT + - "8883:8883" # MQTT over TLS + - "9001:9001" # WebSockets + volumes: + - ./mosquitto:/mosquitto/config + - ${DOCKER_VOLUMES}/mosquitto/data:/mosquitto/data + - ${DOCKER_VOLUMES}/mosquitto/log:/mosquitto/log + networks: + - proxy + labels: + traefik.enable: true + traefik.http.routers.mosquitto.rule: Host(`mosquitto.${MYDOMAIN}`) + traefik.http.routers.mosquitto.middlewares: localaccess@file + traefik.http.services.mosquitto.loadbalancer.server.port: 9001 + homepage.group: Automation + homepage.name: Eclipse Mosquitto + homepage.icon: mosquitto.png + homepage.href: https://mosquitto.${MYDOMAIN}/ + homepage.description: "Open-source MQTT broker" + +networks: + proxy: + external: true diff --git a/docker/automation/mosquitto/mosquitto.conf b/docker/automation/mosquitto/mosquitto.conf new file mode 100644 index 00000000..3f88e66c --- /dev/null +++ b/docker/automation/mosquitto/mosquitto.conf @@ -0,0 +1,36 @@ +# Eclipse Mosquitto Configuration File +# More information: https://mosquitto.org/man/mosquitto-conf-5.html + +# Persistence settings +persistence true +persistence_location /mosquitto/data/ + +# Logging settings +log_dest file /mosquitto/log/mosquitto.log +log_type error +log_type warning +log_type notice +log_type information + +# Connection settings +allow_anonymous true + +# Default MQTT port +listener 1883 + +# WebSocket port for web clients +listener 9001 +protocol websockets + +# TLS/SSL settings (uncomment and configure certificates if needed) +# listener 8883 +# cafile /mosquitto/config/ca.crt +# certfile /mosquitto/config/server.crt +# keyfile /mosquitto/config/server.key + +# Authentication settings (uncomment to enable) +# allow_anonymous false +# password_file /mosquitto/config/passwd + +# Access Control List (uncomment to enable) +# acl_file /mosquitto/config/acl.conf diff --git a/docker/dev/gitea.yaml b/docker/dev/gitea.yaml new file mode 100644 index 00000000..adbb6ebe --- /dev/null +++ b/docker/dev/gitea.yaml @@ -0,0 +1,60 @@ +# Gitea is a lightweight, open-source self-hosted Git service. +# It's a community-managed fork of Gogs with enhanced features focused on Git management and CI/CD integration. +# +# Installation: +# The following commands will output a new SECRET_KEY and INTERNAL_TOKEN to stdout, which you can then place in your environment variables. +# ``` +# docker run -it --rm docker.gitea.com/gitea:1 gitea generate secret SECRET_KEY +# docker run -it --rm docker.gitea.com/gitea:1 gitea generate secret INTERNAL_TOKEN +# ``` +# Do not lose/change your SECRET_KEY after the installation, otherwise the encrypted data can not be decrypted anymore. +# +# 🏠 Home: https://about.gitea.com/ +# 📦 Source: https://github.com/go-gitea/gitea +# 📜 Docs: https://docs.gitea.com/installation/install-with-docker +# +# TODO: Generate SECRET_KEY and INTERNAL_TOKEN for enhanced security +# TODO: Consider switching to PostgreSQL for improved performance with large repositories +--- +name: gitea +services: + gitea: + image: gitea/gitea:1.24.6 + container_name: gitea + restart: unless-stopped + environment: + TZ: ${TIMEZONE} + PUID: ${PUID} + PGID: ${PGID} + USER_UID: ${PUID} + USER_GID: ${PGID} + # Server configuration + GITEA__server__DOMAIN: gitea.${MYDOMAIN} + GITEA__server__ROOT_URL: https://gitea.${MYDOMAIN}/ + # SSH configuration + GITEA__server__SSH_DOMAIN: gitea.${MYDOMAIN} + GITEA__server__SSH_PORT: 222 + GITEA__server__SSH_LISTEN_PORT: 22 + # Security + GITEA__security__SECRET_KEY: ${GITEA_SECRET_KEY} + GITEA__security__INTERNAL_TOKEN: ${GITEA_INTERNAL_TOKEN} + volumes: + - ${DOCKER_VOLUMES}/gitea:/data + networks: + - proxy + ports: + - "222:22" # SSH port for Git operations + labels: + traefik.enable: true + traefik.http.routers.gitea.rule: Host(`gitea.${MYDOMAIN}`) + traefik.http.routers.gitea.middlewares: localaccess@file + traefik.http.services.gitea.loadbalancer.server.port: 3000 + homepage.group: Development + homepage.name: Gitea + homepage.icon: gitea.png + homepage.href: https://gitea.${MYDOMAIN}/ + homepage.description: "Self-hosted Git service" + +networks: + proxy: + external: true diff --git a/docker/guidelines.md b/docker/guidelines.md index 06ba03ac..f9149622 100644 --- a/docker/guidelines.md +++ b/docker/guidelines.md @@ -183,10 +183,12 @@ Persistent data is stored in volumes following this pattern: ```yaml volumes: - - ${DOCKER_VOLUMES}/service-name:/data # Volumes for data storage, not part of the repository - ./service-name/config:/config # Local configuration files, committed to the repository + - ${DOCKER_VOLUMES}/service-name:/data # Volumes for data storage, not part of the repository ``` +For data-heavy applications, like database servers, use named volumes instead of bind mounts. + ## Deployment Workflow Services are deployed using the Taskfile system: @@ -225,6 +227,8 @@ When creating a new service, use this template: # 🏠 Home: https://service-homepage.com/ # 📦 Source: https://github.com/vendor/service # 📜 Docs: https://docs.service.com/ +# +# TODO: List additional configuration tasks, possible enhancements --- name: service-name services: diff --git a/docker/storage/mssql-server.yaml b/docker/storage/mssql-server.yaml new file mode 100644 index 00000000..982d5408 --- /dev/null +++ b/docker/storage/mssql-server.yaml @@ -0,0 +1,39 @@ +# Microsoft SQL Server 2025 - AI-ready enterprise relational database with best-in-class performance and security +# +# 🏠 Home: https://www.microsoft.com/en-us/sql-server +# 📜 Docs: https://learn.microsoft.com/en-us/sql/linux/quickstart-install-connect-docker +# 🐳 Image: https://hub.docker.com/_/microsoft-mssql-server +# +# TODO: Implement health checks to monitor SQL Server availability +# TODO: Set resource limits (CPU and memory) appropriate for workload +# TODO: Implement automated backup strategy +# TODO: Integrate with monitoring solutions (Prometheus, Grafana) for performance metrics +--- +name: mssql-server +services: + mssql-server: + image: mcr.microsoft.com/mssql/server:2025-latest + container_name: mssql-server + hostname: mssql + restart: unless-stopped + # kics-scan ignore-block + environment: + TZ: ${TIMEZONE} + ACCEPT_EULA: Y + MSSQL_PID: Developer + MSSQL_SA_PASSWORD: ${MSSQL_SA_PASSWORD:?MSSQL SA password must be set} + MSSQL_COLLATION: ${MSSQL_COLLATION:-SQL_Latin1_General_CP1_CI_AS} + ports: + - "1433:1433" + volumes: + - mssql_data:/var/opt/mssql + labels: + traefik.enable: false + homepage.group: Storage + homepage.name: Microsoft SQL Server + homepage.icon: microsoft-sql-server.png + homepage.href: mssql://${LOCAL_NODE_IP:-127.0.0.1}:1433 + homepage.description: Enterprise relational database + +volumes: + mssql_data: diff --git a/docker/tools/Taskfile.obsidian.yaml b/docker/tools/Taskfile.obsidian.yaml new file mode 100644 index 00000000..d296382a --- /dev/null +++ b/docker/tools/Taskfile.obsidian.yaml @@ -0,0 +1,52 @@ +version: '3' + +env: + OBSIDIAN_VAULT_NAME: "Today" + +tasks: + repo-sync: + desc: Sync Obsidian vault to git repository + dotenv: ['config/docker/localhost/.env'] + silent: true + cmds: + - | + VAULT_PATH="${STORAGE_OBSIDIAN}/${OBSIDIAN_VAULT_NAME}/" + REPO_NAME="$(printf '%s' "${OBSIDIAN_VAULT_NAME}" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')" + REPO_PATH="$(realpath -s "../${REPO_NAME}")" + echo "Git repository path: ${REPO_PATH}" + + cd "${REPO_PATH}" || { echo "Failed to cd to ${REPO_PATH}"; exit 1; } + if [ ! -d ".git" ]; then + echo "Error: .git folder not found in ${REPO_PATH}" + exit 1 + fi + + rsync --verbose --archive --delete --exclude ".obsidian/" --exclude ".git/" "${VAULT_PATH}" . + git status + + repo-commit: + desc: Commit changes in Obsidian git repository + dotenv: ['config/docker/localhost/.env'] + silent: true + cmds: + - | + REPO_NAME="$(printf '%s' "${OBSIDIAN_VAULT_NAME}" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')" + REPO_PATH="$(realpath -s "../${REPO_NAME}")" + cd "${REPO_PATH}" || { echo "Failed to cd to ${REPO_PATH}"; exit 1; } + git add --all + if git diff --cached --quiet; then + echo "No changes to commit." + else + git commit -m "Automatic sync" + fi + if ! git remote get-url origin 2>/dev/null; then + echo "No remote origin URL is configured. Skipping push." + else + git push + fi + + repo-backup: + desc: Sync Obsidian vault to git repository and commit changes + cmds: + - task: repo-sync + - task: repo-commit diff --git a/docker/tools/obsidian.yaml b/docker/tools/obsidian.yaml new file mode 100644 index 00000000..bb6d4119 --- /dev/null +++ b/docker/tools/obsidian.yaml @@ -0,0 +1,44 @@ +# A powerful knowledge base that works on top of a local folder of plain text Markdown files. +# Obsidian is a free and flexible app for your private thoughts that stores notes locally +# with extensive plugin support and open file formats. +# +# 🏠 Home: https://obsidian.md +# 📦 Source: https://github.com/obsidianmd +# 📜 Docs: https://docs.linuxserver.io/images/docker-obsidian/ +# +# TODO: Set up regular backups of the /config/vaults volume containing vault data +# TODO: Consider GPU acceleration configuration if needed for advanced use cases +--- +name: obsidian +services: + obsidian: + image: lscr.io/linuxserver/obsidian:latest + container_name: obsidian + restart: unless-stopped + environment: + TZ: ${TIMEZONE} + PUID: ${PUID} + PGID: ${PGID} + CUSTOM_USER: ${OBSIDIAN_CUSTOM_USER:-} + PASSWORD: ${OBSIDIAN_PASSWORD:-} + LC_ALL: ${OBSIDIAN_LC_ALL:-} + volumes: + - ${DOCKER_VOLUMES}/obsidian:/config + - ${STORAGE_OBSIDIAN}:/config/vaults + shm_size: "1gb" + networks: + - proxy + labels: + traefik.enable: true + traefik.http.routers.obsidian.rule: Host(`obsidian.${MYDOMAIN}`) + traefik.http.routers.obsidian.middlewares: localaccess-sso@file + traefik.http.services.obsidian.loadbalancer.server.port: 3000 + homepage.group: Tools + homepage.name: Obsidian + homepage.icon: obsidian.png + homepage.href: https://obsidian.${MYDOMAIN}/ + homepage.description: "Knowledge base for Markdown files" + +networks: + proxy: + external: true diff --git a/docs/PRPs/containers/esphome-device-builder.md b/docs/PRPs/containers/esphome-device-builder.md new file mode 100644 index 00000000..cee0d570 --- /dev/null +++ b/docs/PRPs/containers/esphome-device-builder.md @@ -0,0 +1,75 @@ +## Base information for ESPHome Device Builder application + +Application name: ESPHome Device Builder +Homepage: https://esphome.io +GitHub page: https://github.com/esphome/esphome +Install instructions URL: https://esphome.io/guides/getting_started_command_line/#bonus-esphome-device-builder +Container image(s): esphome/esphome:2025.11.0-dev20251010 +Category: automation +Dashboard Icon: esphome.png +Dashboard Group: Automation +Short description: Firmware builder for ESP8266/ESP32 IoT devices +Long description: ESPHome Device Builder is a tool for creating custom firmware for ESP8266/ESP32 microcontrollers through simple YAML configuration files and managing them through Home Automation systems like Home Assistant. + +## Container deployment + +### Docker Compose Example + +```yaml +version: '3' +services: + esphome: + container_name: esphome + image: esphome/esphome:2025.11.0-dev20251010 + volumes: + - ./config:/config + - /etc/localtime:/etc/localtime:ro + ports: + - "6052:6052" + restart: unless-stopped + network_mode: host # Required for device discovery + privileged: true # Required for USB device access + # For USB device passthrough: + devices: + - /dev/ttyUSB0:/dev/ttyUSB0 # Adjust based on your device +``` + +### Environment Variables + +Optional environment variables that can be configured: + +- `ESPHOME_DASHBOARD_USE_PING=true` - Use ping for device status +- `ESPHOME_DASHBOARD_RELATIVE_URL=/esphome` - If behind reverse proxy +- `USERNAME=admin` - Dashboard username (for authentication) +- `PASSWORD=your_password` - Dashboard password (for authentication) + +### Volumes + +- `/config` - Main configuration directory where YAML device configurations are stored +- `/etc/localtime` (optional) - For correct timezone + +### Network Configuration + +- Port `6052` - Web dashboard interface +- `network_mode: host` is recommended for mDNS device discovery +- Alternatively, use bridge mode with port mapping + +### Security Considerations + +1. **Authentication**: Configure dashboard authentication using USERNAME and PASSWORD environment variables when exposing the service to a network. + +2. **USB Access**: The container requires privileged mode or specific device mappings to access USB devices for flashing. + +3. **Network Access**: Consider restricting network access if not using mDNS discovery. + +### Possible Improvements + +1. **Version Pinning**: Pin to specific stable versions in production environments instead of development tags. + +2. **Traefik Integration**: When using Traefik as a reverse proxy, add appropriate labels for automatic routing and TLS. + +3. **Non-privileged Mode**: For improved security, consider configuring with specific device permissions rather than full privileged mode. + +4. **USB Auto-discovery**: For more advanced setups, consider adding udev rules and device management to automatically detect ESP devices. + +5. **Backup Strategy**: Implement regular backups of the configuration directory. diff --git a/docs/PRPs/containers/gitea.md b/docs/PRPs/containers/gitea.md new file mode 100644 index 00000000..af5f4962 --- /dev/null +++ b/docs/PRPs/containers/gitea.md @@ -0,0 +1,159 @@ +## Base information for Gitea application + +Application name: Gitea +Homepage: https://about.gitea.com/ +GitHub page: https://github.com/go-gitea/gitea +Install instructions URL: https://docs.gitea.com/installation/install-with-docker +Container image(s): gitea/gitea:1.24.6 +Category: dev +Dashboard Icon: gitea.png +Dashboard Group: Development +Short description: Self-hosted Git service +Long description: Gitea is a lightweight, open-source self-hosted Git service. It's a painless, community-managed fork of Gogs with enhanced features focused on Git management and CI/CD integration. + +## Container deployment + +### Docker Compose Configuration + +Below is a Docker Compose configuration for deploying Gitea with SQLite (simplest setup): + +```yaml +version: "3" + +networks: + gitea: + external: false + +services: + server: + image: gitea/gitea:1.24.6 + container_name: gitea + environment: + - USER_UID=1000 + - USER_GID=1000 + # Use the following format to override settings in app.ini + # - GITEA__section_name__KEY_NAME=value + restart: always + networks: + - gitea + volumes: + - ./gitea:/data + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + ports: + - "3000:3000" + - "222:22" # Map SSH port to avoid conflicts with host SSH +``` + +### PostgreSQL Configuration + +For improved performance, PostgreSQL is recommended: + +```yaml +version: "3" + +networks: + gitea: + external: false + +services: + server: + image: gitea/gitea:1.24.6 + container_name: gitea + environment: + - USER_UID=1000 + - USER_GID=1000 + - GITEA__database__DB_TYPE=postgres + - GITEA__database__HOST=db:5432 + - GITEA__database__NAME=gitea + - GITEA__database__USER=gitea + - GITEA__database__PASSWD=gitea_password + restart: always + networks: + - gitea + volumes: + - ./gitea:/data + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + ports: + - "3000:3000" + - "222:22" + depends_on: + - db + + db: + image: postgres:15 + restart: always + environment: + - POSTGRES_USER=gitea + - POSTGRES_PASSWORD=gitea_password + - POSTGRES_DB=gitea + networks: + - gitea + volumes: + - ./postgres:/var/lib/postgresql/data +``` + +### Security Considerations + +1. **Secret Key**: Generate a unique secret key: + ```bash + docker run -it --rm gitea/gitea:1.24.6 gitea generate secret SECRET_KEY + ``` + Then set it in your environment: + ```yaml + environment: + - GITEA__security__SECRET_KEY=your_generated_secret + ``` + +2. **HTTPS**: Configure Gitea behind a reverse proxy for HTTPS: + ```yaml + environment: + - GITEA__server__ROOT_URL=https://git.yourdomain.com/ + - GITEA__server__DOMAIN=git.yourdomain.com + ``` + +3. **SSH Access**: If Git SSH access is required, ensure the SSH port is exposed and configured correctly: + ```yaml + environment: + - GITEA__server__SSH_DOMAIN=git.yourdomain.com + - GITEA__server__SSH_PORT=222 + - GITEA__server__SSH_LISTEN_PORT=22 + ``` + +### Important Environment Variables + +- `USER_UID`/`USER_GID`: User/group ID for file ownership (default 1000) +- `GITEA__server__DOMAIN`: Domain name of your Gitea instance +- `GITEA__server__ROOT_URL`: Full URL to access your Gitea instance +- `GITEA__database__DB_TYPE`: Database type (mysql, postgres, mssql, sqlite3) +- `GITEA__security__SECRET_KEY`: Secret for JWT and session encryption +- `GITEA__security__INTERNAL_TOKEN`: Internal API token (generate with the command above) +- `GITEA__server__DISABLE_SSH`: Set to true to disable SSH functionality + +### Volume Management + +Key directories in the `/data` volume: +- `/data/gitea`: Application data +- `/data/git`: Git repositories +- `/data/ssh`: SSH keys and configuration +- `/data/lfs`: Git LFS objects + +### Post-Installation Setup + +After deployment, visit `http://your-server:3000/` to complete the setup: +1. Configure your database settings if not using environment variables +2. Set the site title and other application settings +3. Create the initial admin account + +### Upgrading + +1. Update the image version in your docker-compose.yml +2. Pull the new image: `docker-compose pull` +3. Restart the container: `docker-compose up -d` +4. Check logs for any errors: `docker-compose logs -f` + +It's recommended to back up your data before upgrading: +```bash +cp -r ./gitea ./gitea_backup_$(date +%Y%m%d) +``` diff --git a/docs/PRPs/containers/mosquitto.md b/docs/PRPs/containers/mosquitto.md new file mode 100644 index 00000000..7d461139 --- /dev/null +++ b/docs/PRPs/containers/mosquitto.md @@ -0,0 +1,158 @@ +## Base information for Eclipse Mosquitto application + +Application name: Eclipse Mosquitto +Homepage: https://mosquitto.org/ +GitHub page: https://github.com/eclipse/mosquitto +Install instructions URL: https://hub.docker.com/_/eclipse-mosquitto +Container image(s): eclipse-mosquitto:2.0.22-openssl +Category: automation +Dashboard Icon: https://mosquitto.org/favicon-16x16.png +Dashboard Group: Connections +Short description: Open-source MQTT broker implementing protocol versions 5.0, 3.1.1, and 3.1 +Long description: Eclipse Mosquitto is an open-source MQTT broker implementing versions 5.0, 3.1.1, and 3.1 of the MQTT protocol. It is lightweight and suitable for devices from single board computers to full servers, providing publish/subscribe messaging capabilities with support for TLS, WebSockets, and various authentication methods. + +## Container deployment + +### Basic Docker Compose Configuration + +The most common deployment uses three main ports and requires persistent storage for configuration, data, and logs: + +```yaml +services: + mosquitto: + image: eclipse-mosquitto:2.0.22-openssl + container_name: mosquitto + restart: unless-stopped + ports: + - "1883:1883" # MQTT + - "8883:8883" # MQTTS (secure) + - "9001:9001" # WebSockets + volumes: + - ./mosquitto/config:/mosquitto/config + - ./mosquitto/data:/mosquitto/data + - ./mosquitto/log:/mosquitto/log +``` + +### Advanced Production Setup + +For production environments, consider this enhanced configuration: + +```yaml +version: '3.8' +networks: + mqtt-net: + driver: bridge + +services: + mosquitto: + image: eclipse-mosquitto:2.0.22-openssl + user: mosquitto + volumes: + - type: bind + source: ./config/ + target: /mosquitto/config/ + read_only: false + - type: bind + source: ./log/ + target: /mosquitto/log/ + read_only: false + - type: volume + source: mosquitto-data + target: /mosquitto/data/ + ports: + - "1883:1883" # Standard MQTT + - "8883:8883" # MQTT over TLS + - "9001:9001" # WebSockets + networks: + - mqtt-net + restart: unless-stopped + +volumes: + mosquitto-data: + name: "mosquitto-data" +``` + +### Configuration Setup + +Before deployment, create the required configuration structure: + +1. **Directory Structure**: + ``` + ./mosquitto/ + ├── config/ + │ ├── mosquitto.conf + │ └── passwd (if using authentication) + ├── data/ (for persistence) + └── log/ (for logging) + ``` + +2. **Sample mosquitto.conf**: + ``` + # Persistence settings + persistence true + persistence_location /mosquitto/data/ + + # Logging settings + log_dest file /mosquitto/log/mosquitto.log + + # Authentication settings (uncomment if needed) + # allow_anonymous false + # password_file /mosquitto/config/passwd + + # Default MQTT port + listener 1883 + + # WebSocket port (optional) + listener 9001 + protocol websockets + + # TLS/SSL settings (uncomment and configure if needed) + # listener 8883 + # cafile /mosquitto/config/ca.crt + # certfile /mosquitto/config/server.crt + # keyfile /mosquitto/config/server.key + ``` + +### Environment Variables + +Key environment variables for configuration: +- No specific environment variables are required, but configuration is done through the `mosquitto.conf` file and mounted volumes + +### Security Considerations + +1. **Authentication**: Enable password authentication by setting `allow_anonymous false` and creating a password file +2. **TLS/SSL**: Configure TLS certificates for secure connections on port 8883 +3. **Access Control**: Use ACL (Access Control List) files to restrict topic access per user +4. **Firewall**: Ensure only necessary ports are exposed (1883, 8883, 9001) + +### Authentication Setup + +To add user authentication: + +```bash +# Create password file and add first user +docker exec -it mosquitto mosquitto_passwd -c /mosquitto/config/passwd username1 + +# Add additional users (omit -c flag) +docker exec -it mosquitto mosquitto_passwd /mosquitto/config/passwd username2 +``` + +### Testing the Deployment + +Test the MQTT broker using the included client tools: + +```bash +# Subscribe to a topic +docker exec -it mosquitto mosquitto_sub -t 'test/topic' -v + +# Publish a message (in another terminal) +docker exec -it mosquitto mosquitto_pub -t 'test/topic' -m 'hello world' +``` + +### Performance Tuning + +For high-throughput environments, consider: +- Adjusting `max_connections` in mosquitto.conf +- Configuring `max_queued_messages` for QoS handling +- Setting appropriate `message_size_limit` +- Using persistent sessions strategically diff --git a/docs/PRPs/containers/mssql-server.md b/docs/PRPs/containers/mssql-server.md new file mode 100644 index 00000000..614bba07 --- /dev/null +++ b/docs/PRPs/containers/mssql-server.md @@ -0,0 +1,153 @@ +## Base information for Microsoft SQL Server 2025 application + +Application name: Microsoft SQL Server 2025 +Homepage: https://www.microsoft.com/en-us/sql-server +GitHub page: N/A (proprietary software) +Install instructions URL: https://raw.githubusercontent.com/MicrosoftDocs/sql-docs/refs/heads/live/docs/linux/quickstart-install-connect-docker.md +Container image(s): mcr.microsoft.com/mssql/server:2025-latest +Category: storage +Dashboard Icon: microsoft-sql-server.png +Dashboard Group: Storage +Short description: AI-ready enterprise relational database with best-in-class performance and security +Long description: Microsoft SQL Server is an enterprise-grade relational database management system (RDBMS) with comprehensive data management, business intelligence, and analytics capabilities. SQL Server 2025 is an AI-ready database offering best-in-class security, performance, and availability from ground to cloud. + +## Container deployment + +### Docker Run Command + +Basic deployment using docker run: + +```bash +docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=" \ + -p 1433:1433 --name sql1 --hostname sql1 \ + -d \ + mcr.microsoft.com/mssql/server:2025-latest +``` + +### Environment Variables + +Required environment variables: + +- **ACCEPT_EULA**: Must be set to `Y` to confirm acceptance of the End-User License Agreement +- **MSSQL_SA_PASSWORD**: Sets the system administrator (SA) password. Must meet SQL Server password complexity requirements: + - At least 8 characters + - Contains characters from three of the following categories: + - Uppercase letters (A-Z) + - Lowercase letters (a-z) + - Numbers (0-9) + - Non-alphanumeric symbols (e.g., !, $, #, %) + +Optional environment variables: + +- **MSSQL_COLLATION**: Custom SQL Server collation (default is SQL_Latin1_General_CP1_CI_AS) +- **MSSQL_PID**: SQL Server edition or product key (default is Developer edition) + +### Port Mappings + +- **1433**: SQL Server default TCP port for client connections (host:container mapping should be 1433:1433) + +### Volume Mounts + +For data persistence, it's recommended to mount volumes for: + +- **/var/opt/mssql/data**: Database files +- **/var/opt/mssql/log**: Transaction log files +- **/var/opt/mssql/secrets**: Certificates and keys + +Example with volumes: + +```bash +docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=" \ + -p 1433:1433 --name sql1 --hostname sql1 \ + -v sqldata:/var/opt/mssql/data \ + -v sqllog:/var/opt/mssql/log \ + -v sqlsecrets:/var/opt/mssql/secrets \ + -d \ + mcr.microsoft.com/mssql/server:2025-latest +``` + +### Security Considerations + +1. **Strong Passwords**: Always use strong passwords that meet SQL Server complexity requirements +2. **Change Default SA Password**: The SA account is a well-known privileged account - change the password immediately after deployment +3. **Disable SA Account**: In production environments, it's recommended to disable the SA account and use Windows Authentication or create separate user accounts with appropriate permissions +4. **Network Security**: Consider limiting network access to the SQL Server port (1433) using firewall rules or Docker network configurations +5. **Regular Updates**: Keep the container image updated to receive security patches and bug fixes +6. **Least Privilege**: Run containers with minimal necessary privileges + +### Connectivity + +#### Internal Connection (within container): + +```bash +docker exec -it sql1 /opt/mssql-tools/bin/sqlcmd \ + -S localhost -U SA -P "" +``` + +#### External Connection: + +Connect using the host IP address and port 1433 with various tools: +- SQL Server Management Studio (SSMS) +- Azure Data Studio +- Visual Studio Code with mssql extension +- sqlcmd command-line tool + +### Container Management + +Stop the container: +```bash +docker stop sql1 +``` + +Remove the container: +```bash +docker rm sql1 +``` + +Note: Removing the container without volume mounts will result in data loss. Always use named volumes or bind mounts for production deployments. + +### Docker Compose Example + +```yaml +services: + mssql: + image: mcr.microsoft.com/mssql/server:2025-latest + container_name: mssql-server + hostname: mssql + environment: + - ACCEPT_EULA=Y + - MSSQL_SA_PASSWORD=${MSSQL_SA_PASSWORD} + - MSSQL_PID=Developer + ports: + - "1433:1433" + volumes: + - mssql_data:/var/opt/mssql/data + - mssql_log:/var/opt/mssql/log + - mssql_secrets:/var/opt/mssql/secrets + restart: unless-stopped + +volumes: + mssql_data: + mssql_log: + mssql_secrets: +``` + +### Available Versions + +Microsoft provides SQL Server container images for multiple versions: +- SQL Server 2017 +- SQL Server 2019 +- SQL Server 2022 +- SQL Server 2025 (Preview) + +All versions are based on Ubuntu Linux and available from the Microsoft Container Registry (MCR). + +### Further Improvements + +1. **Health Checks**: Implement Docker health checks to monitor SQL Server availability +2. **Resource Limits**: Set CPU and memory limits appropriate for your workload +3. **Backup Strategy**: Implement automated backup solutions using mounted volumes +4. **Monitoring**: Integrate with monitoring solutions (Prometheus, Grafana) for performance metrics +5. **High Availability**: Consider SQL Server Always On Availability Groups for production environments +6. **Read-Only Replicas**: Deploy read-only replicas for load distribution +7. **Custom Configuration**: Mount custom mssql.conf for advanced SQL Server configuration diff --git a/docs/PRPs/containers/obsidian.md b/docs/PRPs/containers/obsidian.md new file mode 100644 index 00000000..202a04b6 --- /dev/null +++ b/docs/PRPs/containers/obsidian.md @@ -0,0 +1,76 @@ +## Base information for Obsidian application + +Application name: Obsidian +Homepage: https://obsidian.md +GitHub page: https://github.com/obsidianmd +Install instructions URL: https://docs.linuxserver.io/images/docker-obsidian/ +Container image(s): lscr.io/linuxserver/obsidian:latest +Category: tools +Dashboard Icon: obsidian.png +Dashboard Group: Tools +Short description: A powerful knowledge base that works on top of a local folder of plain text Markdown files +Long description: Obsidian is a free and flexible app for your private thoughts that stores notes locally with extensive plugin support and open file formats. It provides a powerful knowledge management system with linking, graph view, and customizable workspace features. + +## Container deployment + +### Docker Compose Configuration + +```yaml +services: + obsidian: + image: lscr.io/linuxserver/obsidian:latest + container_name: obsidian + environment: + - PUID=1000 + - PGID=1000 + - TZ=Etc/UTC + volumes: + - /path/to/config:/config + ports: + - 3000:3000 + - 3001:3001 + shm_size: "1gb" + restart: unless-stopped +``` + +### Environment Variables + +- `PUID`: User ID for file permissions (default: 1000) +- `PGID`: Group ID for file permissions (default: 1000) +- `TZ`: Timezone setting (default: Etc/UTC) +- `CUSTOM_USER` (optional): Username for basic HTTP authentication +- `PASSWORD` (optional): Password for basic HTTP authentication +- `LC_ALL` (optional): Locale setting for internationalization + +### Port Configuration + +- **Port 3000**: HTTP desktop GUI interface (must be proxied for external access) +- **Port 3001**: HTTPS desktop GUI interface + +### Volume Mounts + +- `/config`: User home directory that stores program settings, vault files, and Obsidian configuration + +### Security Considerations + +**Important Security Warning**: This container provides privileged access to the host system. + +- **No built-in authentication**: By default, the container has no authentication mechanism +- **HTTPS requirement**: Full functionality requires HTTPS access +- **Basic authentication**: Optional via `CUSTOM_USER` and `PASSWORD` environment variables +- **Reverse proxy recommended**: Should be placed behind a reverse proxy (like Traefik) when exposed to the internet +- **Network security**: Consider restricting access to trusted networks only + +### Additional Features + +- **GPU acceleration**: Supports hardware acceleration when properly configured +- **Shared memory**: Configured with 1GB shared memory (`shm_size: "1gb"`) for optimal performance +- **Docker-in-Docker**: Optional privileged mode available if needed for advanced use cases + +### Deployment Recommendations + +1. Use a reverse proxy with proper SSL/TLS termination +2. Implement additional authentication layer (e.g., Authelia) for internet exposure +3. Regular backups of the `/config` volume containing vault data +4. Consider setting appropriate user permissions (PUID/PGID) to match host user +5. Monitor resource usage as Obsidian can be resource-intensive with large vaults diff --git a/scripts/labctl.py b/scripts/labctl.py index abc56fe3..e5277dac 100755 --- a/scripts/labctl.py +++ b/scripts/labctl.py @@ -26,11 +26,16 @@ class DockerOptions: """Configuration options for Docker operations.""" pull_before_start: bool = False quiet: bool = False + # Log options + follow: bool = False + tail: str = "all" + since: str | None = None + timestamps: bool = False # Global variables docker_stacks_dir: Path = (Path(__file__).resolve().parent.parent / "docker").resolve() -ALLOWED_STATES: tuple[str, ...] = ('pull', 'up', 'down', 'restart', 'recreate', 'config') +ALLOWED_STATES: tuple[str, ...] = ('pull', 'up', 'down', 'restart', 'recreate', 'config', 'logs') def create_network_if_missing(network_name: str) -> None: @@ -198,6 +203,27 @@ def docker_pull(stack_dir: Path, service_name: str, compose_file: Path, env_file docker(pull_cmd) +def build_log_command_flags(options: DockerOptions) -> list[str]: + """Build Docker Compose log command flags based on options. + + Args: + options: Docker operation options containing log-specific settings + + Returns: + list[str]: List of command flags for docker compose logs + """ + flags = [] + if options.follow: + flags.append("--follow") + if options.tail and options.tail != "all": + flags.extend(["--tail", options.tail]) + if options.since: + flags.extend(["--since", options.since]) + if options.timestamps: + flags.append("--timestamps") + return flags + + def docker_command(host_config_dir: Path, stack_dir: Path, service_name: str, action: str, options: DockerOptions = None) -> None: """Execute Docker Compose command for a service. @@ -205,7 +231,7 @@ def docker_command(host_config_dir: Path, stack_dir: Path, service_name: str, ac host_config_dir: Path to the host-specific configuration directory stack_dir: Path to the service category directory service_name: Name of the service to operate on - action: The action to perform (pull, up, down, restart, recreate, config) + action: The action to perform (pull, up, down, restart, recreate, config, logs) options: Docker operation options (default: None) """ if options is None: @@ -254,6 +280,13 @@ def docker_command(host_config_dir: Path, stack_dir: Path, service_name: str, ac logger.info(f">>> Checking {stack_dir}/{service_name}") docker(["compose", "-f", compose_file, *env_file_args, "config"]) + case "logs": + logger.info(f">>> Showing logs for {stack_dir}/{service_name}") + log_cmd = ["compose", "-f", compose_file, *env_file_args, "logs"] + log_cmd.extend(build_log_command_flags(options)) + log_cmd.append(service_name) + docker(log_cmd) + def load_services_config(config_file: str) -> dict: """Load services configuration from YAML file.""" @@ -349,7 +382,24 @@ def cmd_service(args) -> None: # Everything before the last part is the category path category_path = '/'.join(name_parts[:-1]) - docker_command(get_host_config_dir(), docker_stacks_dir / category_path, service_name, args.operation, DockerOptions(args.pull_before_start, args.quiet)) + # Create options with all parameters + options = DockerOptions( + pull_before_start=args.pull_before_start, + quiet=args.quiet + ) + + # Add log options if they exist in args and operation is 'logs' + if args.operation == 'logs': + if hasattr(args, 'follow'): + options.follow = args.follow + if hasattr(args, 'tail'): + options.tail = args.tail + if hasattr(args, 'since'): + options.since = args.since + if hasattr(args, 'timestamps'): + options.timestamps = args.timestamps + + docker_command(get_host_config_dir(), docker_stacks_dir / category_path, service_name, args.operation, options) def main() -> None: @@ -373,6 +423,11 @@ def main() -> None: service_parser.add_argument('name', help='Service name in format category/name or category/subcategory/name') service_parser.add_argument('--pull-before-start', action='store_true', default=False, help='Pull images before starting the service') service_parser.add_argument('--quiet', action='store_true', default=False, help='Use quiet mode for docker operations') + # Log-specific options + service_parser.add_argument('--follow', '-f', action='store_true', help='Follow log output (like tail -f)') + service_parser.add_argument('--tail', '-n', default="all", help='Number of lines to show from the end of logs (default: all)') + service_parser.add_argument('--since', '-s', help='Show logs since timestamp (e.g., "10m" for last 10 minutes)') + service_parser.add_argument('--timestamps', '-t', action='store_true', help='Show timestamps with log entries') args = parser.parse_args() diff --git a/scripts/create-example-env.py b/scripts/update-example-env.py similarity index 95% rename from scripts/create-example-env.py rename to scripts/update-example-env.py index ecfa1f40..6b1c56dd 100755 --- a/scripts/create-example-env.py +++ b/scripts/update-example-env.py @@ -49,7 +49,7 @@ def mask_sensitive_variables(input_file: str) -> str: def main() -> None: if len(sys.argv) < 2: - print("Usage: ./create-example-env.py ") + print("Usage: ./update-example-env.py ") return input_file = sys.argv[1]