writing-skills
writing-skills
Use when creating new skills, editing existing skills, or verifying skills work before deployment
clickhouse-multi-env-setupConfigure ClickHouse across dev, staging, and production with environment-specific settings, secrets management, and infrastructure-as-code patterns. Use when setting up per-environment ClickHouse instances, managing connection configs, wiring secrets per environment, or deploying to multiple environments. Trigger with "clickhouse environments", "clickhouse…
Independent GitHub snapshot · Automated classification · Original documentation governs
name: clickhouse-multi-env-setup description: | Configure ClickHouse across dev, staging, and production with environment-specific settings, secrets management, and infrastructure-as-code patterns. Use when setting up per-environment ClickHouse instances, managing connection configs, wiring secrets per environment, or deploying to multiple environments. Trigger with "clickhouse environments", "clickhouse dev staging prod", "clickhouse multi-env", "clickhouse environment config", "clickhouse staging setup". allowed-tools: Read, Write, Edit, Bash(aws:), Bash(gcloud:), Bash(vault:*) version: 1.7.0 license: MIT author: Jeremy Longshore jeremy@intentsolutions.io tags:
Configure separate ClickHouse instances for development, staging, and production
with proper secrets management, environment detection, and infrastructure-as-code.
A single typed config module resolves the right instance from NODE_ENV, so
application code never branches on environment and destructive operations are
blocked outside dev/staging.
The full code for every step lives in references/implementation.md; worked end-to-end flows are in references/examples.md.
Provision one instance per tier, isolated by data sensitivity:
| Environment | Instance | Purpose | Data |
|---|---|---|---|
| Development | Docker local | Fast iteration | Synthetic seed data |
| Staging | ClickHouse Cloud (Dev tier) | Pre-prod validation | Sampled prod copy |
| Production | ClickHouse Cloud (Prod tier) | Live traffic | Real data |
Create src/config/clickhouse.ts with one ClickHouseEnvConfig per environment,
keyed by NODE_ENV. Fail fast in non-dev environments — require a password and
enforce HTTPS at startup:
export function getConfig(): ClickHouseEnvConfig {
const env = process.env.NODE_ENV ?? 'development';
const config = configs[env];
if (!config) throw new Error(`Unknown environment: ${env}`);
if (env !== 'development') {
if (!config.password) throw new Error(`CLICKHOUSE_PASSWORD not set for ${env}`);
if (!config.url.startsWith('https://')) {
throw new Error(`ClickHouse ${env} must use HTTPS`);
}
}
return config;
}
Full per-environment config table (pool sizes, timeouts, compression): references/implementation.md.
A lazily-initialized singleton in src/clickhouse/client.ts builds the pool once
per process from the resolved config. Full code:
references/implementation.md.
Dev uses a git-ignored .env.local; every other tier pulls from a secret manager
(AWS Secrets Manager, GCP Secret Manager, or Vault) — never commit credentials.
CLI recipes for all three:
references/implementation.md.
Run sorted .sql files through a migration runner (scripts/apply-schema.ts).
Fail hard in production, log-and-continue in dev/staging:
references/implementation.md.
requireNonProduction blocks TRUNCATE/reset in prod and validateDatabaseName
catches cross-environment queries. Full guards:
references/implementation.md.
Auto-deploy staging; gate production behind needs: deploy-staging plus a
manual-approval environment. Full workflow:
references/examples.md.
Completing this workflow yields:
src/config/clickhouse.ts — typed, per-environment ClickHouse config with
startup validation (password present, HTTPS enforced outside dev).src/clickhouse/client.ts — a singleton client factory keyed off NODE_ENV.scripts/apply-schema.ts — a deterministic, sorted migration runner..github/workflows/deploy.yml pipeline that migrates schema and deploys
staging automatically, production behind manual approval.Application code then calls getClient() with zero environment branching.
| Issue | Cause | Solution |
|---|---|---|
| Wrong database in prod | Env var not set | Validate config on startup |
| TLS error in staging | Using http:// for Cloud | Force https:// in non-dev |
| Schema drift | Applied manually | Use migration runner in CI |
| Secret not found | Missing env var | Check secret manager + CI config |
Query from application code with no environment logic — getConfig() resolves the
right instance from NODE_ENV:
import { getClient } from './clickhouse/client';
const rows = await getClient().query({
query: 'SELECT count() AS c FROM events WHERE date = today()',
format: 'JSONEachRow',
});
console.log(await rows.json());
More worked flows — the environment-strategy layout, the full CI/CD deploy pipeline, and end-to-end query usage — are in references/examples.md.
For monitoring and observability, see the clickhouse-observability skill, which
covers query performance dashboards, slow-query logging, and alerting across the
same dev/staging/production tiers you configured here.