Skip to main content
Permify accepts configuration from three sources — a YAML file, CLI flags, or environment variables. All three are equivalent; use whichever fits your deployment best.
Every CLI flag maps to an environment variable: replace dashes with underscores and prefix with PERMIFY_. For example, --log-level becomes PERMIFY_LOG_LEVEL.

Full example config

account_id: ""

server:
  host: ""
  rate_limit: 100
  http:
    enabled: true
    port: 3476
    grpc_target_host: 127.0.0.1
    tls:
      enabled: false
      cert: /etc/letsencrypt/live/yourdomain.com/fullchain.pem
      key: /etc/letsencrypt/live/yourdomain.com/privkey.pem
  grpc:
    port: 3478
    tls:
      enabled: false
      cert: /etc/letsencrypt/live/yourdomain.com/fullchain.pem
      key: /etc/letsencrypt/live/yourdomain.com/privkey.pem

logger:
  level: info
  file: "" # empty for stdout

profiler:
  enabled: true
  port: 6060

authn:
  enabled: false
  method: preshared
  preshared:
    keys: []

tracer:
  exporter: zipkin
  endpoint: http://localhost:9411/api/v2/spans
  enabled: false

meter:
  exporter: otlp
  endpoint: localhost:4318
  enabled: false

service:
  circuit_breaker: false
  watch:
    enabled: false
  schema:
    cache:
      number_of_counters: 1_000
      max_cost: 10MiB
  permission:
    bulk_limit: 100
    concurrency_limit: 100
    cache:
      number_of_counters: 10_000
      max_cost: 10MiB

database:
  engine: postgres
  uri: postgres://user:password@host:5432/db_name
  auto_migrate: false
  max_connections: 20
  min_connections: 0
  min_idle_connections: 0
  max_connection_lifetime: 300s
  max_connection_idle_time: 60s
  health_check_period: 0s
  max_connection_lifetime_jitter: 0s
  connect_timeout: 0s
  max_data_per_write: 1_000
  max_retries: 10
  watch_buffer_size: 100
  garbage_collection:
    enabled: true
    interval: 200h
    window: 200h
    timeout: 5m

distributed:
  enabled: false
  address: "kubernetes:///permify.default:5000"
  port: "5000"
The full source file is also available in the Permify repository.

Using flags instead of a file

You can pass every option as a CLI flag when you start Permify:
docker run -p 3476:3476 -p 3478:3478 ghcr.io/permify/permify --help

Configuration reference

server

Controls how the HTTP and gRPC servers are exposed.
server
├── host
├── rate_limit
├── http
│   ├── enabled
│   ├── port
│   ├── grpc_target_host
│   └── tls
│       ├── enabled
│       ├── cert
│       └── key
└── grpc
    ├── port
    └── tls
        ├── enabled
        ├── cert
        └── key
server.host
string
default:"\"\""
Host or interface the server binds to. Empty string binds to all interfaces.
server.rate_limit
integer
default:"100"
Maximum number of requests handled per second across the server.
server.http.enabled
boolean
default:"true"
Enable the HTTP/REST API listener.
server.http.port
string
required
Port for the HTTP listener. Default convention is 3476.
server.http.grpc_target_host
string
default:"127.0.0.1"
Host the HTTP gateway uses when forwarding requests to the local gRPC server.
server.http.tls.enabled
boolean
default:"false"
Enable TLS on the HTTP endpoint.
server.http.tls.cert
string
Path to the TLS certificate file for the HTTP endpoint.
server.http.tls.key
string
Path to the TLS private key file for the HTTP endpoint.
server.grpc.port
string
required
Port for the gRPC listener. Default convention is 3478.
server.grpc.tls.enabled
boolean
default:"false"
Enable TLS on the gRPC endpoint.
server.grpc.tls.cert
string
Path to the TLS certificate file for the gRPC endpoint.
server.grpc.tls.key
string
Path to the TLS private key file for the gRPC endpoint.
Environment variables
FlagENVType
server-hostPERMIFY_SERVER_HOSTstring
rate-limitPERMIFY_RATE_LIMITint
grpc-portPERMIFY_GRPC_PORTstring
grpc-tls-enabledPERMIFY_GRPC_TLS_ENABLEDboolean
grpc-tls-cert-pathPERMIFY_GRPC_TLS_CERT_PATHstring
grpc-tls-key-pathPERMIFY_GRPC_TLS_KEY_PATHstring
http-enabledPERMIFY_HTTP_ENABLEDboolean
http-portPERMIFY_HTTP_PORTstring
http-grpc-target-hostPERMIFY_HTTP_GRPC_TARGET_HOSTstring
http-tls-cert-pathPERMIFY_HTTP_TLS_CERT_PATHstring
http-tls-key-pathPERMIFY_HTTP_TLS_KEY_PATHstring
http-cors-allowed-originsPERMIFY_HTTP_CORS_ALLOWED_ORIGINSstring array
http-cors-allowed-headersPERMIFY_HTTP_CORS_ALLOWED_HEADERSstring array

logger

Permify uses zerolog for structured logging.
logger.level
string
default:"info"
required
Log verbosity. Accepted values: error, warn, info, debug.
logger.output
string
default:"text"
Log output format. Accepted values: text, json.
logger.file
string
default:"\"\""
Path to a log file. Defaults to stdout when empty.
Environment variables
FlagENVType
log-levelPERMIFY_LOG_LEVELstring
log-outputPERMIFY_LOG_OUTPUTstring

profiler

Exposes Go’s built-in pprof endpoints for performance analysis.
profiler.enabled
boolean
default:"true"
Enable the pprof profiling HTTP server.
profiler.port
string
default:"6060"
required
Port the pprof server listens on.
When enabled, the following endpoints are available:
EndpointPurpose
GET /debug/pprof/profile?seconds=30CPU profile over a sampling window
GET /debug/pprof/trace?seconds=5Goroutine scheduling and GC trace
GET /debug/pprof/heapHeap allocation snapshot
GET /debug/pprof/goroutineAll goroutines and their stack traces
GET /debug/pprof/Index of all available profiles
# Download and open a 30-second CPU profile
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
The pprof endpoint has no built-in authentication. Restrict network access via firewall rules or a network policy so it is not reachable from the public internet.
Environment variables
FlagENVType
profiler-enabledPERMIFY_PROFILER_ENABLEDboolean
profiler-portPERMIFY_PROFILER_PORTstring

authn

Controls whether API requests must be authenticated. Two methods are supported: pre-shared keys and OpenID Connect.
authn.enabled
boolean
default:"false"
Enable API authentication. When false, all requests are accepted without credentials.
authn.method
string
required
Authentication method. Accepted values: preshared, oidc.

Pre-shared keys

authn:
  enabled: true
  method: preshared
  preshared:
    keys: ["your-secret-key"]
authn.preshared.keys
string[]
required
One or more private keys clients must supply to authenticate. Permify does not generate these keys — you must provide them.

OpenID Connect (OIDC)

authn:
  enabled: true
  method: oidc
  oidc:
    issuer: https://your-idp.example.com
    audience: your-api-audience
    refresh_interval: 15m
    backoff_interval: 12s
    backoff_max_retries: 5
    valid_methods: ["RS256", "HS256"]
authn.oidc.issuer
string
required
URL of the Identity Provider. Used to discover JWKS and validate tokens.
authn.oidc.audience
string
required
Expected audience claim in tokens. Ensures tokens were issued for this service.
authn.oidc.refresh_interval
duration
default:"15m"
How often to refresh the JWKS from the provider.
authn.oidc.backoff_interval
duration
default:"12s"
Initial delay between JWKS fetch retries on failure.
authn.oidc.backoff_frequency
duration
Duration to wait before retrying after a failed authentication attempt.
authn.oidc.backoff_max_retries
integer
default:"5"
Maximum number of JWKS fetch retries before giving up.
authn.oidc.valid_methods
string[]
default:"[\"RS256\", \"HS256\"]"
Allowed JWT signing algorithms. Tokens signed with other algorithms are rejected.
Environment variables
FlagENVType
authn-enabledPERMIFY_AUTHN_ENABLEDboolean
authn-methodPERMIFY_AUTHN_METHODstring
authn-preshared-keysPERMIFY_AUTHN_PRESHARED_KEYSstring array
authn-oidc-issuerPERMIFY_AUTHN_OIDC_ISSUERstring
authn-oidc-audiencePERMIFY_AUTHN_OIDC_AUDIENCEstring
authn-oidc-refresh-intervalPERMIFY_AUTHN_OIDC_REFRESH_INTERVALduration
authn-oidc-backoff-intervalPERMIFY_AUTHN_OIDC_BACKOFF_INTERVALduration
authn-oidc-backoff-frequencyPERMIFY_AUTHN_OIDC_BACKOFF_FREQUENCYduration
authn-oidc-backoff-max-retriesPERMIFY_AUTHN_OIDC_BACKOFF_RETRIESint
authn-oidc-valid-methodsPERMIFY_AUTHN_OIDC_VALID_METHODSstring array

tracer

Permify integrates with Jaeger, Zipkin, SigNoz, and OTLP for distributed tracing.
tracer.enabled
boolean
default:"false"
Enable distributed trace export.
tracer.exporter
string
required
Trace exporter backend. Accepted values: jaeger, otlp, signoz, zipkin.
tracer.endpoint
string
required
Endpoint URL for the trace exporter.
tracer.urlpath
string
default:"/v1/traces"
Override the default URL path for the OTLP exporter.
tracer.insecure
boolean
default:"false"
Use HTTP instead of HTTPS when exporting traces.
Environment variables
FlagENVType
tracer-enabledPERMIFY_TRACER_ENABLEDboolean
tracer-exporterPERMIFY_TRACER_EXPORTERstring
tracer-endpointPERMIFY_TRACER_ENDPOINTstring
tracer-urlpathPERMIFY_TRACER_URL_PATHstring
tracer-insecurePERMIFY_TRACER_INSECUREboolean

meter

Observability metrics: check counts, cache hit rates, and session info (Permify version, hostname, OS, architecture).
meter.enabled
boolean
default:"false"
Enable metrics export.
meter.exporter
string
required
Metrics exporter. otlp is the standard choice.
meter.endpoint
string
required
Endpoint for the metrics exporter.
meter.urlpath
string
default:"/v1/metrics"
Override the default URL path for the OTLP metrics exporter.
meter.insecure
boolean
default:"false"
Use HTTP instead of HTTPS when exporting metrics.
Environment variables
FlagENVType
meter-enabledPERMIFY_METER_ENABLEDboolean
meter-exporterPERMIFY_METER_EXPORTERstring
meter-endpointPERMIFY_METER_ENDPOINTstring
meter-urlpathPERMIFY_METER_URL_PATHstring
meter-insecurePERMIFY_METER_INSECUREboolean

service

Tunes runtime behavior: circuit breaking, cache sizes, and permission evaluation limits.
service.circuit_breaker
boolean
default:"false"
Enable the circuit breaker pattern for internal service calls.
service.watch.enabled
boolean
default:"false"
Enable the configuration watcher for live reloading.
service.schema.cache.number_of_counters
integer
default:"1000"
Number of counters used by the schema cache eviction policy.
service.schema.cache.max_cost
string
default:"10MiB"
Maximum memory budget for the schema cache.
service.permission.bulk_limit
integer
default:"100"
Maximum number of permission checks in a single bulk request.
service.permission.concurrency_limit
integer
default:"100"
Maximum number of concurrent permission evaluations.
service.permission.cache.number_of_counters
integer
default:"10000"
Number of counters used by the permission cache eviction policy.
service.permission.cache.max_cost
string
default:"10MiB"
Maximum memory budget for the permission cache.
Environment variables
FlagENVType
service-circuit-breakerPERMIFY_SERVICE_CIRCUIT_BREAKERboolean
service-watch-enabledPERMIFY_SERVICE_WATCH_ENABLEDboolean
service-schema-cache-number-of-countersPERMIFY_SERVICE_SCHEMA_CACHE_NUMBER_OF_COUNTERSint
service-schema-cache-max-costPERMIFY_SERVICE_SCHEMA_CACHE_MAX_COSTint
service-permission-bulk-limitPERMIFY_SERVICE_PERMISSION_BULK_LIMITint
service-permission-concurrency-limitPERMIFY_SERVICE_PERMISSION_CONCURRENCY_LIMITint
service-permission-cache-max-costPERMIFY_SERVICE_PERMISSION_CACHE_MAX_COSTint

database

Specifies where Permify stores relation tuples, schemas, audit logs, and decision logs.
PostgreSQL 13.8+ is the supported production database. The memory engine is available for local testing only — data is lost on restart.
database.engine
string
default:"memory"
required
Storage backend. Use postgres for production, memory for local development.
database.uri
string
required
PostgreSQL connection URI. Example: postgres://user:password@host:5432/db_name.
database.writer.uri
string
Separate write connection URI. Falls back to uri when not set. Useful for primary/replica setups.
database.reader.uri
string
Separate read connection URI. Falls back to uri when not set.
database.auto_migrate
boolean
default:"true"
Run schema migrations automatically at startup. Set to false if you manage migrations externally.
database.max_connections
integer
default:"20"
Maximum connections in the pool (maps to pgxpool MaxConns). Replaces the deprecated max_open_connections.
database.min_connections
integer
default:"0"
Minimum connections kept in the pool (maps to pgxpool MinConns). If 0 and max_idle_connections is set, that value is used instead.
database.min_idle_connections
integer
default:"0"
Minimum idle connections in the pool (maps to pgxpool MinIdleConns).
database.max_connection_lifetime
duration
default:"300s"
Maximum lifetime of a connection before it is closed and replaced.
database.max_connection_idle_time
duration
default:"60s"
Maximum time a connection may remain idle before it is closed.
database.health_check_period
duration
default:"0s"
Interval between health checks on idle connections. 0 uses the pgxpool default of 1 minute.
database.max_connection_lifetime_jitter
duration
default:"0s"
Random jitter added to max_connection_lifetime to prevent thundering-herd connection expiry. 0 defaults to 20% of max_connection_lifetime.
database.connect_timeout
duration
default:"0s"
Maximum time to wait when establishing a new connection. 0 uses the pgx default (no timeout).
database.max_data_per_write
integer
default:"1000"
Maximum number of relation tuples written in a single database operation.
database.max_retries
integer
default:"10"
Maximum number of retries for failed database operations.
database.watch_buffer_size
integer
default:"100"
Buffer size for change-stream watch operations.

database.garbage_collection

Periodically removes stale relation tuples to keep the database size under control.
database.garbage_collection.enabled
boolean
default:"false"
Enable the garbage collection job.
database.garbage_collection.interval
duration
default:"200h"
How often the garbage collection job runs.
database.garbage_collection.window
duration
default:"200h"
How far back in time the job looks when cleaning stale data.
database.garbage_collection.timeout
duration
default:"5m"
Maximum time a single garbage collection run may take.

Production connection pooling with pgcat

For multi-replica deployments, connect Permify through pgcat (a PostgreSQL connection pooler) rather than directly to Postgres. This prevents connection exhaustion when many Permify pods are running.
database:
  engine: postgres
  writer:
    uri: postgresql://postgres:DB_PASSWORD@pgcat:6432/permify?plan_cache_mode=force_custom_plan&default_query_exec_mode=cache_describe
  reader:
    uri: postgresql://postgres:DB_PASSWORD@pgcat:6432/permify?plan_cache_mode=force_custom_plan&default_query_exec_mode=cache_describe
  max_connections: 1   # Keep low — pgcat manages the real pool
  min_connections: 0
Environment variables
FlagENVType
database-enginePERMIFY_DATABASE_ENGINEstring
database-uriPERMIFY_DATABASE_URIstring
database-writer-uriPERMIFY_DATABASE_WRITER_URIstring
database-reader-uriPERMIFY_DATABASE_READER_URIstring
database-auto-migratePERMIFY_DATABASE_AUTO_MIGRATEboolean
database-max-connectionsPERMIFY_DATABASE_MAX_CONNECTIONSint
database-min-connectionsPERMIFY_DATABASE_MIN_CONNECTIONSint
database-min-idle-connectionsPERMIFY_DATABASE_MIN_IDLE_CONNECTIONSint
database-max-connection-lifetimePERMIFY_DATABASE_MAX_CONNECTION_LIFETIMEduration
database-max-connection-idle-timePERMIFY_DATABASE_MAX_CONNECTION_IDLE_TIMEduration
database-health-check-periodPERMIFY_DATABASE_HEALTH_CHECK_PERIODduration
database-max-connection-lifetime-jitterPERMIFY_DATABASE_MAX_CONNECTION_LIFETIME_JITTERduration
database-connect-timeoutPERMIFY_DATABASE_CONNECT_TIMEOUTduration
database-max-data-per-writePERMIFY_DATABASE_MAX_DATA_PER_WRITEint
database-max-retriesPERMIFY_DATABASE_MAX_RETRIESint
database-watch-buffer-sizePERMIFY_DATABASE_WATCH_BUFFER_SIZEint
database-garbage-collection-enabledPERMIFY_DATABASE_GARBAGE_COLLECTION_ENABLEDboolean
database-garbage-collection-intervalPERMIFY_DATABASE_GARBAGE_COLLECTION_INTERVALduration
database-garbage-collection-timeoutPERMIFY_DATABASE_GARBAGE_COLLECTION_TIMEOUTduration
database-garbage-collection-windowPERMIFY_DATABASE_GARBAGE_COLLECTION_WINDOWduration

distributed

Enables consistent hashing across multiple Permify instances so that permission cache lookups are routed to the same pod for a given key, improving cache efficiency.
distributed.enabled
boolean
default:"false"
Enable distributed mode.
distributed.address
string
Address of the distributed service. For Kubernetes, use the DNS name of the headless service, e.g. kubernetes:///permify.default:5000.
distributed.port
string
default:"5000"
Port the distributed service is exposed on.
Environment variables
FlagENVType
distributed-enabledPERMIFY_DISTRIBUTED_ENABLEDboolean
distributed-addressPERMIFY_DISTRIBUTED_ADDRESSstring
distributed-portPERMIFY_DISTRIBUTED_PORTstring

Build docs developers (and LLMs) love