This repo contains a Helm chart to deploy anycable-go websocket server.
To install anycable-go to your kubernetes cluster simply run:
helm repo add anycable https://helm.anycable.io/
helm upgrade -i anycable-go anycable/anycable-goAnyCable allows you to use any WebSocket server (written in any language) as a replacement for your Ruby server (such as Faye, ActionCable, etc).
AnyCable uses the same protocol as ActionCable, so you can use its JavaScript client without any monkey-patching.
anycable-go is an implementations of AnyCable WebSocket server written in Go. This repository allows you to install anycable-go into your Kubernetes cluster.
helm repo add anycable https://helm.anycable.io/
helm upgrade -i anycable-go anycable/anycable-goThe command deploys anycable-go on the Kubernetes cluster in the default configuration. The configuration section lists various ways to override default configuration during deployment.
Tip: To list all releases use
helm list
helm delete anycable-goThe command removes all the Kubernetes components associated with the chart and deletes the release.
Specify each parameter using the --set key=value[,key=value] argument to helm install or provide your own file via -f values.yaml. For example,
helm upgrade -i anycable-go \
--namespace anycable-go \
--set image.tag=1.6.6 \
anycable/anycable-goThe above command installs a specified version of anycable.
These are the values used to configure anycable-go itself:
| Value | Description | Default |
|---|---|---|
| image.repository | Choose between anycable/anycable-go and ghcr.io/anycable/anycable-go-pro |
anycable/anycable-go |
| image.tag | Version of docker image to use | 1.6.3 |
| image.pullSecrets.enabled | Enable creating secret for pulling image from AnyCable Pro private registry | false |
| image.pullSecrets.registry | URL of a private registry you want to authorize to | ghcr.io |
| image.pullSecrets.username | Github username | `` |
| image.pullSecrets.password | Github token | `` |
| env.anycableHost | Listen IP address or host | 0.0.0.0 |
| env.anycablePort | Listen port number | 8080 |
| env.anycablePath | WebSocket endpoint path (you can specify multiple paths using a comma as separator) | /cable |
| env.anycableSecret | The application secret used to secure AnyCable features: signed streams, JWT authentication, etc. | `` |
| env.anycableStreamsSecret | A dedicated secret key used to sign streams. If none specified, the application secret is used. | `` |
| env.anycableJwtSecret | The secret key used to sign JWT tokens. Optional (the application secret is used if no JWT secret specified) | `` |
| env.anycableJwtTtl | The time-to-live (TTL) for tokens in seconds. | 3600 |
| env.anycableBroadcastAdapter | Broadcasting adapter to use: redis, http, redisx or nats (multiple are allowed via a comma-separated list) | redis |
| env.anycableBroadcastKey | A secret key used to authorize broadcast requests. Currently, only used by the HTTP adapter. If not set, the value is inferred from the application secret. | `` |
| env.anycableHttpBroadcastPath | HTTP pub/sub endpoint path | /_broadcast |
| env.anycableHttpBroadcastPort | Port to receive broadcasting requests | 8090 |
| env.anycableHttpBroadcastSecret | Authorization secret to protect the broadcasting endpoint | |
| env.anycableRedisUrl | Redis DB url | redis://localhost:6379/5 |
| env.anycableRedisChannel | Redis channel for broadcasts | __anycable__ |
| env.anycableRedisSentinels | Comma-separated list of sentinel hosts, format: :password@hostname:port,.. |
|
| env.anycableRedisSentinelDiscoveryInterval | Interval to rediscover sentinels in seconds | 30 |
| env.anycableRedisKeepaliveInterval | Interval to periodically ping Redis to make sure it's alive | 30 |
| env.anycableRedisTlsVerify | Whether to validate Redis server TLS certificate if rediss:// protocol is used | false |
| env.anycableRedisTlsClientCertPath | Path to file with client TLS certificate in PEM format if Redis server requires client authentication | |
| env.anycableRedisTlsClientKeyPath | Path to file with private key for client TLS certificate if Redis server requires client authentication | |
| env.anycableRedisDisableCache | Some managed Redis (e.g., Google Cloud) providers block many Redis commands, including client-side server tracking, which is enabled in AnyCable by default. See here for more details | false |
| env.anycableRpcConcurrency | Max number of concurrent RPC request; should be slightly less than the RPC server concurrency | 28 |
| env.anycableRpcHost | RPC service address | localhost:50051 |
| env.anycableRpcEnableTls | Enable client-side TLS with the RPC server | false |
| env.anycableRpcTlsVerify | Whether to verify the RPC server certificate | true |
| env.anycableRpcTlsRootCa | CA root certificate file path or contents in PEM format | If not set, system CAs will be used |
| env.anycableRpcMaxCallRecvSize | Override default MaxCallRecvMsgSize for RPC client (bytes) | |
| env.anycableRpcMaxCallSendSize | Override default MaxCallSendMsgSize for RPC client (bytes) | |
| env.anycableRpcImpl | RPC implementation (grpc, http) | |
| env.anycableHttpRpcSecret | Authentication secret for RPC over HTTP | |
| env.anycableHttpRpcTimeout | HTTP RPC timeout (in ms) | 3000 |
| env.anycableHeaders | List of headers to proxy to RPC | cookie |
| env.anycableProxyCookies | Cookie keys to send to RPC, default is all | |
| env.anycablePresets | Configuration presets, comma-separated (none, fly, heroku, broker) | Inferred automatically if possible |
| env.anycableBroker | Broker engine to use (none, memory, nats, redis) | `` |
| env.anycableHistoryLimit | Max number of messages to keep in the stream's history | 100 |
| env.anycableHistoryTtl | TTL for messages in stream's history (seconds) | 300 |
| env.anycableSessionsTtl | TTL for expired/disconnected sessions (seconds) | 300 |
| env.anycablePubsub | Pub/Sub adapter to use (none, redis, nats) | `` |
| env.anycableNatsServers | Comma-separated list of NATS cluster servers | nats://localhost:4222 |
| env.anycableNatsChannel | NATS channel for broadcasts | __anycable__ |
| env.anycableNatsDontRandomizeServers | Disable NATS servers randomization during (re-)connect | false |
| env.anycableEmbedNats | Enable embedded NATS server and use it for pub/sub | |
| env.anycableEnatsAddr | NATS server bind address | nats://localhost:4222 |
| env.anycableEnatsCluster | NATS cluster service bind address | |
| env.anycableEnatsClusterName | NATS cluster name | anycable-cluster |
| env.anycableEnatsClusterRoutes | Comma-separated list of known cluster addresses | |
| env.anycableEnatsGateway | NATS gateway bind address | |
| env.anycableEnatsGateways | Semicolon-separated list of known gateway configurations | |
| env.anycableEnatsGatewayAdvertise | NATS gateway advertise address | |
| env.anycableEnatsStoreDir | Embedded NATS store directory for JetStream | If not set, auto-generated in the temp dir |
| env.anycableEnatsServerName | Embedded NATS unique server name | If not set, auto-generated |
| env.anycableEnatsDebug | Enable NATS server logs | |
| env.anycableEnatsTrace | Enable NATS server protocol trace logs | |
| env.anycableMaxMessageSize | Maximum size of a message in bytes | 65536 |
| env.anycableMaxConn | Limit simultaneous server connections | 0 (no limit) |
| env.anycableShutdownTimeout | Graceful shutdown timeout (in seconds) | 30 |
| env.anycableDisconnectTimeout | [DEPRECATED] Graceful shutdown timeout (in seconds). Use env.anycableShutdownTimeout instead |
5 |
| env.anycableDisconnectRate | Max number of Disconnect calls per second | 100 |
| env.anycableShutdownPoolSize | Number of goroutines to use for disconnect calls on shutdown | 16 |
| env.anycableDisconnectBacklogSize | Size of the channel's buffer for disconnect requests | 4096 |
| env.anycableDisconnectMode | Define when to call Disconnect callback: auto, always, never | auto |
| env.anycableDisableDisconnect | [DEPRECATED] Disable calling Disconnect callback. Use env.anycableDisconnectMode=never instead |
false |
| env.anycableReadBufferSize | WebSocket connection read buffer size | 1024 |
| env.anycableWriteBufferSize | WebSocket connection write buffer size | 1024 |
| env.anycableEnableWsCompression | Enable WebSocket per message compression | false |
| env.anycableHubGopoolSize | Size of the goroutines pool to broadcast messages | 16 |
| env.anycableAllowedOrigins | Accept requests only from specified origins. No check is performed if empty | |
| env.anycablePingInterval | Action Cable ping interval (in seconds) | 3 |
| env.anycablePingTimestampPrecision | Precision for timestamps in ping messages (s, ms, ns) | s |
| env.anycablePongTimeout | How long to wait for a pong response before disconnecting the client (in seconds). Zero means no pongs required | 0 |
| env.anycableJwtIdKey | Encryption key used to verify JWT tokens | |
| env.anycableJwtIdParam | Name of a query string param or an HTTP header carrying a token | jid |
| env.anycableJwtIdEnforce | Whether to enforce token presence for all connections | false |
| env.anycableTurboStreams | Enable Turbo Streams | false |
| env.anycableTurboRailsKey | Enable Turbo Streams fastlane with the specified signing key | |
| env.anycableTurboRailsCleartext | Enable Turbo Streams fastlane without stream names signing | false |
| env.anycableCableReadyKey | Enable CableReady fastlane with the specified signing key | |
| env.anycableCableReadyCleartext | Enable Cable Ready fastlane without stream names signing | false |
| env.anycableSse | Enable SSE endpoint | false |
| env.anycableSsePath | SSE endpoint path | /events |
| command | Override entrypoint for Alpine images | /usr/local/bin/anycable-go |
| args | Customize ARGV (in addition to env settings above) |
[] |
| Value | Description | Default |
|---|---|---|
| replicas | Number of replicas for anycable-go deployment (ignored when HPA is enabled) |
1 |
|containerSecurityContext.enabled|Enables container's Security Context|false|
|containerSecurityContext.runAsUser|Sets webhook containers' Security Context runAsUser|1001|
|containerSecurityContext.runAsGroup|Sets webhook containers' Security Context runAsGroup|1001|
|containerSecurityContext.runAsNonRoot|Sets webhook containers' Security Context runAsNonRoot|true|
|containerSecurityContext.privileged|Sets webhook container's Security Context privileged|false|
|containerSecurityContext.allowPrivilegeEscalation|Sets webhook container's Security Context allowPrivilegeEscalation|false|
|containerSecurityContext.capabilities.drop|Sets webhook container's Security Context capabilities.drop|["ALL"]|
|containerSecurityContext.seccompProfile.type|Sets webhook container's Security Context seccompProfile.type|"RuntimeDefault"|
|containerSecurityContext.readOnlyRootFilesystem|Sets webhook container's Security Context readOnlyRootFilesystem|true|
|hpa.enabled|Enable HorizontalPodAutoscaler|false|
|hpa.minReplicas|Minimum replicas for HPA|1|
|hpa.maxReplicas|Maximum replicas for HPA|3|
|hpa.targetCPUUtilizationPercentage|Target CPU utilization for HPA|50|
|pod.annotations|User-specified Pod annotations|{}|
|pod.extraLabels|User-specified Pod Labels|{}|
|pod.priorityClassName|Controller pod priority class name|""|
|pod.runtimeClassName|Name of the runtime class to be used by pod(s)|""|
|pod.schedulerName|Name of the k8s scheduler (other than default)|""|
|pod.securityContext.enabled|Enables Controller pods' Security Context|false|
|pod.securityContext.fsGroupChangePolicy|Set filesystem group change policy|"Always"|
|pod.securityContext.sysctls|Set kernel settings using the sysctl interface|[]|
|pod.securityContext.supplementalGroups|Set filesystem extra groups|[]|
|pod.securityContext.fsGroup|Set Controller pod's Security Context fsGroupo|1001|
|pod.serviceAccountName|User-specified ServiceAccount for Pod identity|""|
|pod.tolerations|User-specified Pod tolerations|[]|
|pod.topologySpreadConstraints|Topology Spread Constraints for pod assignment|[]|
|pod.disruptionBudget.enabled|Enables podDisruptionBudget configuration|true|
|pod.disruptionBudget.minUnavailable|podDisruptionBudget minimum number unavailable pods|"50%"|
|pod.disruptionBudget.maxUnavailable|podDisruptionBudget maximum number of unavailable pods|""|
|service.annotations|User-specified Service annotations|{}|
| Value | Description | Default |
|---|---|---|
| env.anycableLogLevel | Set logging level (debug/info/warn/error/fatal) | info |
| env.anycableLogFormat | Set logging format (text, json) | text |
| env.anycableDebug | Enable debug mode (more verbose logging) | |
| env.anycableHealthPath | HTTP health endpoint path | /health |
| env.anycableMetricsLog | Enable metrics logging (with info level) | |
| env.anycableMetricsRotateInterval | Specify how often to flush metrics to writers (logs, statsd) (in seconds) | 15 |
| env.anycableMetricsLogInterval | [DEPRECATED] Specify how often flush metrics logs. Use env.anycableMetricsRotateInterval instead |
15 |
| env.anycableMetricsLogFormatter | Specify the path to custom Ruby formatter script (only supported on MacOS and Linux) | |
| env.anycableMetricsHttp | Enable HTTP metrics endpoint at the specified path | /metrics |
| env.anycableMetricsHost | Server host for metrics endpoint, default: the same as for main server | |
| env.anycableMetricsPort | Server port for metrics endpoint, default: the same as for main server | 8081 |
| env.anycableMetricsTags | Comma-separated list of default (global) tags to add to every metric | |
| env.anycableMetricsLogFilter | Specify list of metrics to print to log | |
| env.anycableStatsRefreshInterval | How often to refresh the server stats (in seconds) | 5 |
| env.anycableStatsdHost | Server host for metrics sent to statsd server | |
| env.anycableStatsdPrefix | Statsd metrics prefix | anycable_go. |
| env.anycableStatsdMaxPacketSize | Statsd client maximum UDP packet size | 1400 |
| env.anycableStatsdTagsFormat | One of "datadog", "influxdb", or "graphite" | datadog |
| serviceMonitor.enabled | Enable creation of ServiceMonitor resource for automatic metrics discovery by Prometheus operator |
false |
| serviceMonitor.namespace | Namespace to create ServiceMonitor in (if differs from chart's namespace) | chart's namespace |
| serviceMonitor.interval | Metrics scrape interval | Prometheus default |
| serviceMonitor.labels | Labels that should be present in service monitor to be discovered | {} |
| Value | Description | Default |
|---|---|---|
| ingress.enabled | Enable ingress resource for anycable-go | false |
| ingress.className | Ingress class name (e.g., nginx, traefik) |
"" |
| ingress.acme.enabled | Enable cert-manager integration for automatic Let's Encrypt certificates | false |
| ingress.acme.clusterIssuer | cert-manager ClusterIssuer to use for ACME certificates, empty means try cluster default one | |
| ingress.annotations | Additional annotations for the ingress resource (merged with ACME annotations) | {} |
| ingress.hosts | List of host configurations with paths (see example below) | [] |
| ingress.tls | TLS configuration (optional if acme.enabled is true - auto-generated) |
[] |
Example configurations:
# Minimal HTTPS with Let's Encrypt (recommended)
ingress:
enabled: true
acme:
enabled: true
hosts:
- host: anycable.example.com
paths:
- path: /cable
# Multiple hosts with custom annotations
ingress:
enabled: true
className: nginx
acme:
enabled: true
clusterIssuer: letsencrypt-staging
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "32m"
hosts:
- host: anycable.example.com
paths:
- path: /cable
pathType: Prefix
- host: ws.example.com
paths:
- path: /cable
pathType: Prefix
# Manual TLS (without cert-manager)
ingress:
enabled: true
className: nginx
hosts:
- host: anycable.example.com
paths:
- path: /cable
pathType: ImplementationSpecific
tls:
- secretName: anycable-tls-secret
hosts:
- anycable.example.comNotes:
- When
acme.enabled: true, TLS configuration is auto-generated if not explicitly provided - Auto-generated TLS secret name:
{{ $fullName }}-go-tls - All hosts from
ingress.hostsare automatically included in the TLS certificate - Requires cert-manager to be installed in your cluster for ACME
Only use this if you need encryption between the ingress controller and anycable-go pods (internal cluster encryption). For standard HTTPS ingress, use ingress.tls configuration above.
| Value | Description | Default |
|---|---|---|
| tls.secretName | Name of TLS secret containing certificate and key for pod-to-pod encryption | "" |
| tls.crt | Full certificate chain in PEM format to create new TLS secret for pod encryption | "" |
| tls.key | Private key in PEM format to create new TLS secret for pod encryption | "" |
Example - encrypting between ingress-nginx and anycable-go:
tls:
secretName: "anycable-go-internal-tls"
crt: |
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAKJ3Hs8fPQRNMA0GCSqGSIb3DQEBCwUAMEUxCzAJ...
-----END CERTIFICATE-----
key: |
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7VJTUt9Us...
-----END PRIVATE KEY-----Note: If you only provide secretName without crt/key, the secret must already exist in your cluster.
When a new version of anycable become available, the chart can be not updated yet. In this case you would need to add new environment variables to the application. These variables can be added to the env.custom section of values. Under the hood, custom env variables are passed to the container through the secret.
# ...
env:
# ...
custom:
NEW_FEATURE: foobar-
Obtain certificate/key pair for anycable-go and CA root certificate for ingress-nginx from your CA.
For testing purposes you can generate your own CA with following commands:
# Generate one-time private CA openssl req -x509 -sha256 -newkey rsa:4096 -keyout ca.key -out ca.crt -days 356 -nodes -subj '/CN=Fake CA' # Issue certificate for anycable-go using it openssl req -new -newkey rsa:4096 -keyout server.key -out server.csr -nodes -subj '/CN=anycable-go' openssl x509 -req -sha256 -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt
See kubernetes/ingress-nginx#4503 (comment) for details.
-
Place private CA root certificate into separate secret containing only
ca.crtkey:kubectl create secret generic ca-private --from-file=ca.crt=ca.crt
See kubernetes/ingress-nginx#4688 for details
Place certificate/key pair for anycable-go into separate TLS secret
kubectl create secret tls anycable-go-tls-private --from-file=tls.crt=server.crt --from-file=tls.key=server.key
-
Reference secret with CA root certificate in the
nginx.ingress.kubernetes.io/proxy-ssl-secretannotation (with namespace) -
Specify
HTTPSas upstream protocol innginx.ingress.kubernetes.io/backend-protocolannotation. -
Specify anycable-go certificate's
CN(or one ofAltSubjectNames) inproxy_ssl_namedirective in configuration snippet. Nginx sends the content of$proxy_hostvariable by default and in nginx-ingress always sendsupstream_balanceras hostname in SNI extension and we need override it. -
Enable upstream SSL verification by setting
nginx.ingress.kubernetes.io/proxy-ssl-verifyannotation to'on'
Complete example:
# values.yaml
fullNameOverride: anycable-go
ingress:
enable: true
path: /cable
acme:
hosts:
- example.com
annotations:
nginx.ingress.kubernetes.io/backend-protocol: HTTPS
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_ssl_name anycable-go;
nginx.ingress.kubernetes.io/proxy-ssl-secret: default/ca-private
nginx.ingress.kubernetes.io/proxy-ssl-verify: 'on'
tls:
secretName: anycable-go-tls-
Clone this repository
-
Update your chart, bump version in its
Chart.yaml -
Lint you chart:
helm lint anycable-go
-
Commit and push your changes:
git commit -m "anycable-go: vX.Y.Z" git tag anycable-go-X.Y.Z -s -m "anycable-go chart vX.Y.Z for app version A.B.C" git push --follow-tags
-
Enjoy!