Skip to content

Environment Configuration

This guide is for configuration your running process reads at startup: log levels, feature flags, service URLs, database credentials, and API keys. It is not a deployment guide and it does not create a separate staging or production environment. Deploy each environment as its own app, then target that app by name.

Environment configuration changes the Deployment’s container environment. It does not rebuild the image or change the app’s Service, hostname, or Gateway API HTTPRoute.

Use For Visibility
1ctl config create Non-sensitive runtime values such as LOG_LEVEL, feature flags, and internal URLs Plain text in a Kubernetes ConfigMap and visible to people who can inspect configuration
1ctl secret create Passwords, tokens, connection strings, and private keys Stored as a Kubernetes Secret; the normal table output shows metadata without values

Do not put either kind of runtime value in satusky.toml. That file describes the application to deploy; these commands configure an application that already exists.

Deploy the app first, then set its name once for the commands below:

Terminal window
APP=my-api
1ctl app get "$APP"

Use --app "$APP" in scripts when you do not want the current directory or a config file to decide which deployment is changed. --deployment-id is the equivalent explicit target when you have the UUID.

config create is an upsert: keys you supply are added or updated, and unrelated keys remain in place.

Terminal window
1ctl config create --app "$APP" \
--env APP_ENV=production \
--env LOG_LEVEL=warn \
--env FEATURE_SEARCH_V2=enabled

List the configuration for only this app:

Terminal window
1ctl config list --app "$APP"

The CLI list output identifies the configuration and deployment. It may include non-secret values, so do not use it for credentials.

Add a secret without putting its value in the command

Section titled “Add a secret without putting its value in the command”

Read the secret into your shell without echoing it, pass it once, then remove the shell variable. Replace DATABASE_URL with the key your application expects.

Terminal window
read -r -s -p 'Database URL: ' DATABASE_URL
printf '\n'
1ctl secret create --app "$APP" --kv "DATABASE_URL=$DATABASE_URL"
unset DATABASE_URL

secret create upserts only the supplied keys, so it is also the safe way to rotate one credential without replacing other secrets. Config and secret changes reconcile a fresh pod template. Wait for the application to become healthy; use an explicit restart only when you intentionally want another rollout:

Terminal window
1ctl app status "$APP" --watch

Do not paste real secrets into shell history, source files, satusky.toml, or CI logs. In CI, provide a protected CI variable and pass it with the same quoted --kv "KEY=$VARIABLE" form.

List secret metadata by app. This is a metadata/count check, not a way to retrieve a value:

Terminal window
1ctl secret list --app "$APP"

Treat this as secret-group metadata only. It does not retrieve a value and should not be used to prove that a particular key reached the process. Use the value-free presence checks below.

Prove the new pod received the configuration

Section titled “Prove the new pod received the configuration”

1ctl app status --watch reports the current workload state, but it does not identify a particular configuration generation. After the rollout is healthy, an operator with Kubernetes access should wait for the Deployment and then check the references and test only whether a secret is present. The following never prints the secret value.

Terminal window
NAMESPACE=<your-organization-namespace>
kubectl -n "$NAMESPACE" rollout status "deployment/$APP" --timeout=5m
# The deployed container should reference both stores by key.
kubectl -n "$NAMESPACE" get deployment "$APP" -o json | \
jq -r '.spec.template.spec.containers[].env[] |
select(.name == "APP_ENV" or .name == "DATABASE_URL") |
if .valueFrom.configMapKeyRef then
"\(.name): ConfigMap reference"
elif .valueFrom.secretKeyRef then
"\(.name): Secret reference"
else
"\(.name): unexpected direct value"
end'
# The non-secret value is exact; the secret check reports presence only.
POD=$(kubectl -n "$NAMESPACE" get pod -l "app=$APP" \
-o jsonpath='{.items[0].metadata.name}')
kubectl -n "$NAMESPACE" exec "$POD" -- sh -c \
'[ "$APP_ENV" = production ] && echo "APP_ENV injected"; \
[ -n "$DATABASE_URL" ] && echo "DATABASE_URL present"'

On Gateway API installations, you can separately confirm that the route is still present; runtime configuration must not create or replace it:

Terminal window
kubectl -n "$NAMESPACE" get httproute

Some clusters are still completing the Gateway API migration and may expose an Ingress during that compatibility period. Do not edit either routing resource to configure environment variables or secrets.

Remove exactly the key you no longer need, then wait for the reconciled rollout so new pods cannot retain a stale process environment:

Terminal window
1ctl config unset --app "$APP" --key FEATURE_SEARCH_V2
1ctl secret unset --app "$APP" --key DATABASE_URL

config unset and secret unset remove the selected key from desired state and the Deployment reference. They do not delete the complete configuration bundle or unrelated keys. For Infisical-backed secrets, the materialized Kubernetes Secret is updated on the operator’s next resync; use the key-presence check above before treating credential rotation or removal as complete.

Use distinct app names for staging and production, for example my-api-staging and my-api. Each app has its own config and secret bundles. You can still keep separate satusky.staging.toml and satusky.production.toml files for image, resource, and domain settings, but use --app or --deployment-id when changing runtime configuration so the target is unambiguous.

  • Deploy the target app before creating config or secrets.
  • Use config create only for values safe to expose as plain text.
  • Use secret create for credentials; never retrieve or print their values to verify them.
  • Wait for the reconciled rollout after a config or secret change; restart only when explicitly needed.
  • Verify Kubernetes references and secret presence without reading secret data.
  • Keep routing separate: configuration changes should leave the app’s Service and HTTPRoute untouched.