1---
2title: Variables
3description: Config.Redacted on a Service, Railway.Variable when Railway should own the value.
4---
5
6Most secrets in a [`Service`](/railway/compute/services) come from
7your `.env`. Yield `Config.Redacted` in the constructor. Alchemy binds the
8value onto the Service.
9
10Use [`Railway.Variable`](/providers/railway/variable) when the value
11should be shared across services and managed in one place by
12Railway. The plaintext is never stored in attributes.
13
14## Bind from `.env`
15
16```typescript
17import * as Config from "effect/Config";
18import * as Redacted from "effect/Redacted";
19
20export default class Api extends Railway.Service<Api>()(
21 "Api",
22 {
23 project: Site,
24 main: import.meta.url,
25 port: 3000,
26 },
27 Effect.gen(function* () {
28 const apiKey = yield* Config.Redacted("API_KEY");
29
30 return {
31 fetch: Effect.gen(function* () {
32 const token = Redacted.value(apiKey);
33 // ...
34 }),
35 };
36 }),
37) {}
38```
39
40`Config.Redacted("API_KEY")` reads `API_KEY` from the env of whoever
41runs the deploy and writes it onto the Service. At runtime the same
42line resolves from that env var.
43
44The value is `Redacted<string>`. Unwrap with `Redacted.value` only
45where you need the raw string. Do not pass `env: { ... }` on a
46Service. Yield Config.
47
48See [Secrets & Config](/environments/secrets) for combinators and
49stages.
50
51## When Railway should own the variable
52
53`Railway.Variable` is injected as an environment variable into
54services in that environment. Use it when the value is shared and
55managed in one place, not when only this Service reads a deploy-time
56`.env` key.
57
58```typescript
59import * as Railway from "alchemy/Railway";
60import * as Redacted from "effect/Redacted";
61
62export const ApiToken = Railway.Variable("ApiToken", {
63 project: Site,
64 name: "API_TOKEN",
65 value: Redacted.make("…"),
66});
67```
68
69`name` is the env-var services see. It is stored as-is
70(case-sensitive). Omit it and Alchemy generates an ownership-stamped
71name. Updating `value` is in place via `variableUpsert`. Changing
72`project`, `environment`, `service`, or `name` replaces.
73
74The resolved resource exposes `name` and `digest`. Never the
75plaintext.
76
77Shared (no `service`) variables apply to every service in the
78environment. Pass `service` to scope one Variable to one Service.
79
80```typescript
81const token = yield* Railway.Variable("ApiToken", {
82 project: site,
83 service: api,
84 name: "API_TOKEN",
85 value: Redacted.make("sk_live_…"),
86});
87```
88
89## Read a Variable from a Service
90
91Railway injects the Variable as env. Read it with `Config` — never
92log the plaintext.
93
94```typescript
95export default class Api extends Railway.Service<Api>()(
96 "Api",
97 {
98 project: Site,
99 main: import.meta.url,
100 port: 3000,
101 },
102 Effect.gen(function* () {
103 yield* ApiToken;
104 const name = yield* Config.String("API_TOKEN").pipe(
105 Effect.orElseSucceed(() => ""),
106 );
107
108 return {
109 fetch: Effect.succeed(
110 HttpServerResponse.json({ name: "API_TOKEN", set: name.length > 0 }),
111 ),
112 };
113 }),
114) {}
115```
116
117Yield the Variable in the Stack (or from the Service's constructor) so it is
118part of the graph.
119
120## Variable references
121
122[`Railway.ref`](/providers/railway/ref) emits a Railway template
123(`${{Service.KEY}}` or `${{shared.NAME}}`). Store it as a Variable
124`value` or a `Service.env` entry. Railway keeps the template
125(`unrendered: true`) and interpolates it — it is not a resolved
126URI. Distinct from the unexported resource-prop `Ref<T>`.
127
128Use `ConnectPostgres` / `ConnectMySQL` / `ConnectMongo` for a typed
129client inside an Effect-native Service. Use `Railway.ref` when you
130want Railway's own interpolation (IaC `db.env.DATABASE_URL`,
131`ctx.shared.SENTRY_DSN`).
132
133```typescript
134const db = yield* Railway.Postgres("Db", { project: site });
135yield* Railway.Variable("DatabaseUrl", {
136 project: site,
137 service: api,
138 name: "DATABASE_URL",
139 value: Railway.ref(db, "DATABASE_URL"),
140});
141```
142
143Shared variables use the `"shared"` namespace:
144
145```typescript
146yield* Railway.Variable("ApiSentry", {
147 project: site,
148 service: api,
149 name: "SENTRY_DSN",
150 value: Railway.ref("shared", "SENTRY_DSN"),
151});
152```
153
154Railway interpolates by service **name**. Set `name` on
155Postgres/Service to the LogicalId if the template must resolve to
156that service.
157
158## Environment
159
160Defaults to the Project's primary environment. Pass a
161[Environment](/railway/compute/environments) to target another one.
162
163```typescript
164const staging = yield* Railway.Environment("Staging", { project: site });
165const token = yield* Railway.Variable("StagingToken", {
166 project: site,
167 environment: staging,
168 value: Redacted.make("sk_staging_…"),
169});
170```
171
172## Where next
173
174The [tutorial](/railway/tutorial/part-4) stores a Variable and reads
175it from a Service. [Services](/railway/compute/services) covers
176`Config.Redacted` for values from `.env`. [Postgres](/railway/data/postgres),
177[MySQL](/railway/data/mysql), [Mongo](/railway/data/mongo),
178[Redis](/railway/data/redis), and [Buckets](/railway/data/buckets)
179bind typed clients (`ConnectPostgres`, `ConnectMySQL`,
180`ConnectMongo`, `ReadWriteRedis`, `PutObject`) that use Railway-owned
181variables internally. See the
182[`Variable` reference](/providers/railway/variable) and
183[`ref` reference](/providers/railway/ref).