Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
a243b99
feat: make config and login more robust and convenient
ionicsolutions Apr 10, 2025
f16f668
feat: implement nebu serve --docker
ionicsolutions Apr 11, 2025
3c37d6d
chore: split and simplify configuration
ionicsolutions Apr 15, 2025
e6648ea
dev: add redis to dev setup
ionicsolutions Apr 15, 2025
6b33a6a
chore: finalize config separation
ionicsolutions Apr 16, 2025
426071c
Fix headscale service annotations
ionicsolutions Mar 28, 2025
c13c314
Configure deployment for tailscale
ionicsolutions Mar 28, 2025
4a9959f
Improve headscale configurability
ionicsolutions Mar 31, 2025
0ea184e
Simplify Ingress resources
ionicsolutions Mar 31, 2025
acce6b7
Add LoadBalancer service and Let's Encrypt support
ionicsolutions Apr 1, 2025
2902d5a
Fix Let's Encrypt configuration
ionicsolutions Apr 1, 2025
675a953
Bump Helm chart v0.2.1 to nebulous to 0.1.75
ionicsolutions Apr 3, 2025
389501b
fix: drop Ingress for Redis
ionicsolutions Apr 15, 2025
d74efb4
feat: Redis on tailnet
ionicsolutions Apr 15, 2025
c233f1a
Unify spelling of values
ionicsolutions Apr 16, 2025
0d83e0f
fix: make auth server port configurable
ionicsolutions Apr 17, 2025
6a9c614
feat: headscale commands
ionicsolutions Apr 17, 2025
82c4f44
feat: Add headscale helper commands to CLI
ionicsolutions Apr 18, 2025
2585481
Sketch Docker-based container platform
ionicsolutions Apr 18, 2025
7fd02ca
sketch out controllers and models
ionicsolutions Apr 19, 2025
b7ee6df
Sketch out container controller and nebulous as a container platform
ionicsolutions Apr 21, 2025
1a912dd
Persist tailscale state for Redis
ionicsolutions Apr 22, 2025
49c1445
Use a separate auth key for redis
ionicsolutions Apr 22, 2025
55c6e5d
Manage creation of tailscale secrets
ionicsolutions Apr 22, 2025
341e16a
Add instructions on keys and secrets to chart README
ionicsolutions Apr 22, 2025
dac7972
Simplify tailscale setup for Redis and fix nebu SA
ionicsolutions Apr 22, 2025
e0afb64
Add bucket and root owner to deployment
ionicsolutions Apr 22, 2025
58747c4
Restore instructions in README
ionicsolutions Apr 22, 2025
b22810d
Set new bucket name and root owner defaults
ionicsolutions Apr 22, 2025
4fd8804
feat: switch to canonical tailscale setup for nebulous and redis
ionicsolutions Apr 23, 2025
0f91ed0
feat: HTTP and REST connection
ionicsolutions Apr 24, 2025
dd0a02c
Add support for OpenMeter and extra env vars
ionicsolutions Apr 28, 2025
f45af27
Expose tailscale socket to nebulous container
ionicsolutions Apr 29, 2025
6b0c9b7
Expose sidecar's tailscaled socket to nebu container
ionicsolutions Apr 29, 2025
d4059b0
Add Redis password to connection string
ionicsolutions Apr 30, 2025
dee0764
Add Runpod container registry auth ID
ionicsolutions Apr 30, 2025
de562ba
Add references for tailscale integration
ionicsolutions Apr 30, 2025
32d3f7d
Place tailscale containers last
ionicsolutions May 2, 2025
020f100
WIP
ionicsolutions Jun 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions deploy/charts/nebulous/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ home: https://github.com/agentsea/nebulous
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.2.0
version: 0.2.2

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "0.1.61"
appVersion: "0.1.86"
106 changes: 90 additions & 16 deletions deploy/charts/nebulous/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# nebulous

![Version: 0.2.0](https://img.shields.io/badge/Version-0.2.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.1.61](https://img.shields.io/badge/AppVersion-0.1.61-informational?style=flat-square)
![Version: 0.2.1](https://img.shields.io/badge/Version-0.2.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.1.75](https://img.shields.io/badge/AppVersion-0.1.75-informational?style=flat-square)

A cross-cloud container orchestrator for AI workloads

Expand All @@ -19,6 +19,27 @@ encryptionKey:
encodedValue: "<base64 encoded key>"
```

Add the Tailscale API key and auth key:
```yaml
tailscale:
apiKey: <Tailscale API key>
authKey: <Tailscale auth key for Nebulous>
```

The integrated Redis database requires an auth key for Tailscale as well:
```yaml
redis:
create: true
tailscale:
authKey: <Tailscale auth key for Redis>
```

Finally, enable the creation of the integrated Postgres database:
```yaml
postgres:
create: true
```

Add the nebulous chart repository and install the chart into a dedicated namespace:

```bash
Expand All @@ -27,24 +48,65 @@ helm install nebulous nebulous/nebulous -f values.yaml \
--namespace nebulous --create-namespace
```

## Credential secrets

In production, the encryption key and Tailscale keys should be provided as Kubernetes secrets
and not as Helm chart values.

You can use the following template to create them.
This template assumes installation in the `nebulous` namespace
and the secret names and keys as defined in the Helm chart's default [values.yaml](./values.yaml).

```yaml
apiVersion: v1
kind: Secret
metadata:
name: nebulous-secret
namespace: nebulous
data:
ENCRYPTION_KEY: <base64 encoded key>
---
apiVersion: v1
kind: Secret
metadata:
name: tailscale-secret
namespace: nebulous
stringData:
API_KEY: "<Tailscale API key>"
AUTH_KEY: "<Tailscale auth key for Nebulous>"
---
apiVersion: v1
kind: Secret
metadata:
name: tailscale-redis-secret
namespace: nebulous
data:
AUTH_KEY: "<Tailscale auth key for Redis>"
```

## Values

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| bucket.name | string | `"nebulous-rs"` | The name of the bucket to use for Nebulous. |
| bucket.region | string | `"us-east-1"` | The region of the bucket to use for Nebulous. |
| encryptionKey.encodedValue | string | `""` | The 32 byte encryption key encoded in base64. Not recommended for production. |
| encryptionKey.secret.keys.encryptionKey | string | `"ENCRYPTION_KEY"` | The key in the secret containing the encryption key. |
| encryptionKey.secret.name | string | `"nebulous-secret"` | The name of the secret containing the 32 byte encryption key. |
| extraEnv | list | `[]` | Additional environment variables to pass to the Nebulous server container. |
| headscale.create | bool | `false` | If true, create a Headscale deployment and service. Overrides tailscale configuration. Not recommended for production. |
| headscale.derp | object | `{"configMap":{"key":"servers.yaml","name":""},"externalMaps":[]}` | The Headscale DERP configuration. Either 'externalMapUrls' or 'configMap' must be set. |
| headscale.derp.configMap.key | string | `"servers.yaml"` | The key in the ConfigMap containing the DERP server configuration YAML file. |
| headscale.derp.configMap.name | string | `""` | The name of the ConfigMap containing the DERP server configuration. |
| headscale.derp.externalMaps | list | `[]` | URLs of externally available DERP maps encoded in JSON. |
| headscale.dns.base_domain | string | `""` | The base domain for MagicDNS hostnames. Cannot be the same as the Headscale server's domain. Refer to https://github.com/juanfont/headscale/blob/main/config-example.yaml for details. |
| headscale.domain | string | `""` | The domain under which the Headscale server is exposed. |
| headscale.dns.baseDomain | string | `""` | The base domain for MagicDNS hostnames. Cannot be the same as the Headscale server's domain. Refer to https://github.com/juanfont/headscale/blob/main/config-example.yaml for details. |
| headscale.domain | string | `""` | The domain under which the Headscale server is exposed. Required if create is true. The headscale server must be reachable at https://${domain}:443. |
| headscale.imageTag | string | `"latest"` | The Headscale image tag. |
| headscale.ingress.annotations | object | `{}` | Annotations to add to the Ingress resource. |
| headscale.ingress.enabled | bool | `false` | If enabled, create an Ingress resource. Ignored unless 'enabled' is true. |
| headscale.ingress.ingressClassName | string | `""` | The ingress class. |
| headscale.log.format | string | `"text"` | The log format of the Headscale server. Options are "text" or "json". |
| headscale.log.level | string | `"info"` | The log level of the Headscale server. Options are "off", "trace", "debug", "info", "warn", "error". |
| headscale.namespaceOverride | string | `""` | Namespace override for the Headscale deployment. |
| headscale.prefixes | object | `{"v4":"100.64.0.0/10","v6":"fd7a:115c:a1e0::/48"}` | Prefixes to allocate tailaddresses from. Must be within the IP ranges supported by the Tailscale client. Refer to https://github.com/juanfont/headscale/blob/main/config-example.yaml for details. |
| headscale.privateKeys.claimName | string | `"headscale-keys-pvc"` | The name of the PersistentVolumeClaim for the Headscale private keys. |
Expand All @@ -54,21 +116,32 @@ helm install nebulous nebulous/nebulous -f values.yaml \
| headscale.service.annotations | object | `{}` | The annotations to add to the Kubernetes service. |
| headscale.service.nameOverride | string | `""` | Override the name of the Kubernetes service. |
| headscale.service.port | int | `80` | The port of the Kubernetes service. |
| headscale.service.type | string | `"ClusterIP"` | The type of the Kubernetes service. Options are "ClusterIP", "NodePort", and "LoadBalancer". |
| headscale.sqlite.claimName | string | `"headscale-sqlite-pvc"` | The name of the PersistentVolumeClaim for the Headscale sqlite database. |
| headscale.sqlite.createPersistentVolumeClaim | bool | `true` | If true, create a PersistentVolumeClaim for the Headscale sqlite database. |
| headscale.sqlite.size | string | `"10Gi"` | The size of the PersistentVolumeClaim created for the Headscale sqlite database. |
| headscale.sqlite.storageClassName | string | `""` | The storage class of the PersistentVolumeClaim created for the Headscale sqlite database. |
| headscale.tls.letsencrypt.claimName | string | `"headscale-tls-pvc"` | The name of the PersistentVolumeClaim for the Headscale Let's Encrypt cache. |
| headscale.tls.letsencrypt.createPersistentVolumeClaim | bool | `true` | If true, create a PersistentVolumeClaim for the Headscale Let's Encrypt cache. |
| headscale.tls.letsencrypt.email | string | `""` | The email address for the Let's Encrypt certificate. |
| headscale.tls.letsencrypt.hostname | string | `""` | The hostname for the Let's Encrypt certificate. Has to match the domain of the Headscale server. |
| headscale.tls.letsencrypt.size | string | `"16Mi"` | The size of the PersistentVolumeClaim created for the Headscale Let's Encrypt cache. |
| headscale.tls.letsencrypt.storageClassName | string | `""` | The storage class of the PersistentVolumeClaim created for the Headscale Let's Encrypt cache. |
| image.pullPolicy | string | `"IfNotPresent"` | |
| image.repository | string | `"us-docker.pkg.dev/agentsea-dev/nebulous/server"` | The repository to pull the server image from. |
| image.tag | string | `""` | The nebulous image tag. Defaults to the Helm chart's appVersion. |
| ingress.annotations | object | `{}` | Annotations to add to the Ingress resource. |
| ingress.enabled | bool | `false` | If enabled, create an Ingress resource. |
| ingress.host | string | `""` | The host field of the Ingress rule. |
| ingress.ingressClassName | string | `""` | The ingress class. |
| local.enabled | bool | `false` | If enabled, nebulous can run Pods in the local cluster. |
| logLevel | string | `"info"` | The log level of the Nebulous server. Options are "off", "trace", "debug", "info", "warn", "error". |
| messageQueue.type | string | `"redis"` | The message queue type. The currently only supported value is "redis". |
| namespaceOverride | string | `""` | Override the namespace. By default, Nebulous is deployed to the Helm release's namespace. |
| openmeter.enabled | bool | `false` | Enable usage monitoring with OpenMeter. |
| openmeter.secret.keys.token | string | `"TOKEN"` | The key in the eecret containing the OpenMeter API token. |
| openmeter.secret.name | string | `"openmeter-secret"` | The name of the secrets containing the OpenMeter API token. |
| openmeter.token | string | `""` | The OpenMeter API token. Not recommended for production. |
| openmeter.url | string | `"https://openmeter.cloud"` | The URL to report OpenMeter data to. |
| postgres.auth | object | `{"database":"nebulous","host":"","password":"nebulous","port":5432,"user":"nebulous"}` | Manual configuration of the Postgres connection. Except for 'host', this information is also used if 'create' is true. |
| postgres.create | bool | `false` | If enabled, create a Postgres deployment and service. Not recommended for production. |
| postgres.imageTag | string | `"latest"` | The postgres image tag. Ignored unless 'create' is true. |
Expand All @@ -77,29 +150,31 @@ helm install nebulous nebulous/nebulous -f values.yaml \
| postgres.persistence.enabled | bool | `false` | If enabled, use a PersistentVolumeClaim for the Postgres data. Ignored unless 'create' is true. |
| postgres.persistence.size | string | `"100Gi"` | The size of the PersistentVolumeClaim for the Postgres data. |
| postgres.persistence.storageClassName | string | `""` | The storage class of the PersistentVolumeClaim for the Postgres data. |
| postgres.secret.keys.connection_string | string | `"CONNECTION_STRING"` | The key in the secret containing the Postgres connection string. |
| postgres.secret.keys.connectionString | string | `"CONNECTION_STRING"` | The key in the secret containing the Postgres connection string. |
| postgres.secret.name | string | `"postgres-secret"` | Name of the secret with the Postgres connection string. |
| providers.aws.auth | object | `{"accessKeyId":"","secretAccessKey":""}` | Manual configuration of the AWS credentials. Not recommended for production. |
| providers.aws.enabled | bool | `false` | Enable access to AWS. |
| providers.aws.secret.keys.accessKeyId | string | `"AWS_ACCESS_KEY_ID"` | The key in the secret containing the access key ID. |
| providers.aws.secret.keys.secretAccessKey | string | `"AWS_SECRET_ACCESS_KEY"` | The key in the secret containing the secret access key. |
| providers.aws.secret.name | string | `"aws-secret"` | The name of the secret containing the AWS credentials. |
| providers.runpod.auth | object | `{"apiKey":""}` | Manual configuration of the Runpod API key. Not recommended for production. |
| providers.runpod.auth | object | `{"apiKey":"","containerRegistryAuthId":""}` | Manual configuration of the Runpod credentials. Not recommended for production. |
| providers.runpod.enabled | bool | `false` | Enable access to Runpod. |
| providers.runpod.secret.keys.apiKey | string | `"RUNPOD_API_KEY"` | The key in the secret containing the API key. |
| providers.runpod.secret.name | string | `"runpod-secret"` | The name of the secret containing the API key. |
| providers.runpod.secret.keys.containerRegistryAuthId | string | `"RUNPOD_CONTAINER_REGISTRY_AUTH_ID"` | The key in the secret containing the container registry auth ID. |
| providers.runpod.secret.name | string | `"runpod-secret"` | The name of the secret containing the Runpod credentials. |
| redis.auth | object | `{"database":0,"host":"","password":"nebulous","port":6379}` | Manual configuration of the Redis connection. Except for 'host', this information is also used if 'create' is true. |
| redis.create | bool | `false` | If enabled, create a Redis deployment and service. Not recommended for production. |
| redis.imageTag | string | `"latest"` | The redis image tag. Ignored unless 'create' is true. |
| redis.ingress.annotations | object | `{}` | Annotations to add to the Ingress resource. |
| redis.ingress.enabled | bool | `false` | If enabled, create an Ingress resource. Ignored unless 'create' is true. |
| redis.ingress.host | string | `""` | The host field of the Ingress rule. |
| redis.ingress.ingressClassName | string | `""` | The ingress class. |
| redis.secret.keys.connection_string | string | `"CONNECTION_STRING"` | The key in the secret containing the Redis connection string. |
| redis.secret.keys.connectionString | string | `"CONNECTION_STRING"` | The key in the secret containing the Redis connection string. |
| redis.secret.keys.password | string | `"PASSWORD"` | The key in the secret containing the Redis password. |
| redis.secret.name | string | `"redis-secret"` | Name of the secret with the Redis connection string and password. |
| redis.service.annotations | object | `{}` | The annotations to add to the Kubernetes service. |
| redis.service.nameOverride | string | `""` | Override the name of the Kubernetes service. |
| redis.serviceAccountName | string | `"redis"` | The name of the Kubernetes service account for the Redis Pod. |
| redis.tailscale.authKey | string | `""` | The Tailscale auth key for Redis. If headscale.enabled is true, this is ignored. |
| redis.tailscale.secret.keys.authKey | string | `"AUTH_KEY"` | The key in the secret containing the Tailscale auth key. |
| redis.tailscale.secret.name | string | `"tailscale-redis-secret"` | Name of the secret with the Tailscale auth key for Redis. |
| rootOwner | string | `"agentsea"` | The owner of the Nebulous root. |
| service.annotations | object | `{}` | Annotations to add to the Kubernetes service. |
| service.nameOverride | string | `""` | Override the name of the Kubernetes service. |
| service.port | int | `3000` | The port of the Kubernetes service. |
Expand All @@ -123,8 +198,7 @@ helm install nebulous nebulous/nebulous -f values.yaml \
| tailscale.apiKey | string | `""` | The Tailscale API key. If headscale.enabled is true, this is ignored. |
| tailscale.authKey | string | `""` | The Tailscale auth key. If headscale.enabled is true, this is ignored. |
| tailscale.loginServer | string | `"https://login.tailscale.com"` | The Tailscale host to connect to. If headscale.enabled is true, this is ignored. |
| tailscale.secret.keys.apiKey | string | `"API_KEY"` | The key in the secret containing the Tailscale API key |
| tailscale.secret.keys.authKey | string | `"AUTH_KEY"` | The key in the secret containing the Tailscale auth key |
| tailscale.secret.keys.loginServer | string | `"LOGIN_SERVER"` | The key in the secret containing the Tailscale host. |
| tailscale.secret.name | string | `"tailscale-secret"` | Name of the secret with the Redis connection string and password. |
| tailscale.secret.keys.apiKey | string | `"API_KEY"` | The key in the secret containing the Tailscale API key. |
| tailscale.secret.keys.authKey | string | `"AUTH_KEY"` | The key in the secret containing the Tailscale auth key. |
| tailscale.secret.name | string | `"tailscale-secret"` | Name of the secret with the Tailscale auth key and API key. |

57 changes: 57 additions & 0 deletions deploy/charts/nebulous/README.md.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,27 @@ encryptionKey:
encodedValue: "<base64 encoded key>"
```

Add the Tailscale API key and auth key:
```yaml
tailscale:
apiKey: <Tailscale API key>
authKey: <Tailscale auth key for Nebulous>
```

The integrated Redis database requires an auth key for Tailscale as well:
```yaml
redis:
create: true
tailscale:
authKey: <Tailscale auth key for Redis>
```

Finally, enable the creation of the integrated Postgres database:
```yaml
postgres:
create: true
```

Add the nebulous chart repository and install the chart into a dedicated namespace:

```bash
Expand All @@ -29,6 +50,42 @@ helm install nebulous {{ $appName }}/{{ template "chart.name" . }} -f values.yam
--namespace nebulous --create-namespace
```

## Credential secrets

In production, the encryption key and Tailscale keys should be provided as Kubernetes secrets
and not as Helm chart values.

You can use the following template to create them.
This template assumes installation in the `nebulous` namespace
and the secret names and keys as defined in the Helm chart's default [values.yaml](./values.yaml).

```yaml
apiVersion: v1
kind: Secret
metadata:
name: nebulous-secret
namespace: nebulous
data:
ENCRYPTION_KEY: <base64 encoded key>
---
apiVersion: v1
kind: Secret
metadata:
name: tailscale-secret
namespace: nebulous
stringData:
API_KEY: "<Tailscale API key>"
AUTH_KEY: "<Tailscale auth key for Nebulous>"
---
apiVersion: v1
kind: Secret
metadata:
name: tailscale-redis-secret
namespace: nebulous
data:
AUTH_KEY: "<Tailscale auth key for Redis>"
```

{{ template "chart.valuesSection" . }}

{{ template "helm-docs.versionFooter" . }}
9 changes: 0 additions & 9 deletions deploy/charts/nebulous/templates/NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,6 @@ WARNING: Using the integrated Postgres database. This is not recommended for pro
{{- end }}
{{ if .Values.redis.create }}
Internal Redis endpoint: {{ printf "redis://%s:%d" (include "redis.host" . ) (int .Values.redis.auth.port) }}
{{- if .Values.redis.ingress.enabled }}
{{- if .Values.redis.ingress.host }}
External Redis endpoint: {{ printf "redis://%s" .Values.redis.ingress.host }}
{{- else }}
External Redis endpoint: .Values.redis.ingress.host not specified, refer to Ingress configuration provided through values.yaml.
{{- end }}
{{- else }}
External Redis endpoint: Set .Values.redis.ingress.enabled to true to expose Redis externally.
{{- end }}
{{- end }}
{{ if .Values.encryptionKey.encodedValue }}
WARNING: Encryption key is not provided through a user-managed secret. This is not recommended for production.
Expand Down
18 changes: 17 additions & 1 deletion deploy/charts/nebulous/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ app.kubernetes.io/instance: {{ .Release.Name | trunc 63 | trimSuffix "-" }}
{{- printf "%s-local-role" .Release.Name }}
{{- end }}

{{- define "nebulous.tailscaleStateSecretName" -}}
tailscale-{{- include "nebulous.serviceAccountName" . }}-state-secret
{{- end }}

{{- define "headscale.name" -}}
headscale
{{- end }}
Expand All @@ -46,7 +50,15 @@ headscale
{{- end }}

{{- define "headscale.host" -}}
{{- include "headscale.serviceName" . }}.{{- include "headscale.namespace" . }}.svc.cluster.local
https://{{- required ".Values.headscale.domain is required" .Values.headscale.domain }}
{{- end }}

{{- define "tailscale.loginServer" }}
{{- if .Values.headscale.create }}
{{- include "headscale.host" . }}
{{- else }}
{{- required ".Values.tailscale.loginServer is required" .Values.tailscale.loginServer }}
{{- end }}
{{- end }}

{{- define "postgres.name" -}}
Expand Down Expand Up @@ -76,3 +88,7 @@ redis
{{- required ".Values.redis.auth.host is required" .Values.redis.auth.host }}
{{- end }}
{{- end }}

{{- define "redis.tailscaleStateSecretName" -}}
tailscale-{{- include "redis.name" . }}-state-secret
{{- end }}
Loading