|
1 | 1 | --- |
| 2 | +sidebar_label: Edge Proxy |
2 | 3 | title: Edge Proxy |
3 | 4 | sidebar_position: 3 |
4 | 5 | --- |
5 | 6 |
|
6 | | -The Flagsmith Edge Proxy is a service that you host yourself, which allows you to run an instance of the Flagsmith Engine close to your servers. If you are running Flagsmith within a server-side environment and you want to have very low latency flags, you have two options: |
| 7 | +The [Edge Proxy](/performance/edge-proxy) runs as a |
| 8 | +[Docker container](https://hub.docker.com/repository/docker/flagsmith/edge-proxy) with no external dependencies. |
| 9 | +It connects to the Flagsmith API to download environment documents, and your Flagsmith client applications connect to it |
| 10 | +using [remote flag evaluation](/integrating-with-flagsmith/sdks#remote-evaluation). |
7 | 11 |
|
8 | | -1. Run the Edge Proxy within your own infrastructure and connect to it from your server-side SDKs |
9 | | -2. Run your server-side SDKs in [Local Evaluation Mode](/integrating-with-flagsmith/sdks/server-side#local-evaluation). |
| 12 | +The examples below assume you have a configuration file located at `./config.json`. Your Flagsmith client applications |
| 13 | +can then consume the Edge Proxy by setting their API URL to `http://localhost:8000/api/v1/`. |
10 | 14 |
|
11 | | -The main benefit to running the Edge Proxy is that you reduce your polling requests against the Flagsmith API itself. |
| 15 | +<details> |
| 16 | +<summary>Docker CLI</summary> |
| 17 | +``` |
| 18 | +docker run \ |
| 19 | + -v ./config.json:/app/config.json \ |
| 20 | + -p 8000:8000 \ |
| 21 | + flagsmith/edge-proxy:latest |
| 22 | +``` |
| 23 | +</details> |
| 24 | + |
| 25 | +<details> |
| 26 | +<summary>Docker Compose</summary> |
| 27 | +```yaml title="compose.yaml" |
| 28 | +services: |
| 29 | + edge_proxy: |
| 30 | + image: flagsmith/edge-proxy:latest |
| 31 | + volumes: |
| 32 | + - type: bind |
| 33 | + source: ./config.json |
| 34 | + target: /app/config.json |
| 35 | + ports: |
| 36 | + - '8000:8000' |
| 37 | +``` |
| 38 | +</details> |
| 39 | +
|
| 40 | +## Configuration |
| 41 | +
|
| 42 | +The Edge Proxy can be configured with any combination of: |
| 43 | +
|
| 44 | +- Environment variables. |
| 45 | +- A JSON configuration file, by default located at `/app/config.json` in the Edge Proxy container. |
| 46 | + |
| 47 | +Environment variables take priority over their corresponding options defined in the configuration file. |
| 48 | + |
| 49 | +Environment variable names are case-insensitive, and are processed using |
| 50 | +[Pydantic](https://docs.pydantic.dev/2.7/concepts/pydantic_settings/#environment-variable-names). |
| 51 | + |
| 52 | +### Example configuration |
| 53 | + |
| 54 | +<details> |
| 55 | + |
| 56 | +<summary>Configuration file</summary> |
| 57 | + |
| 58 | +```json title="/app/config.json" |
| 59 | +{ |
| 60 | + "environment_key_pairs": [ |
| 61 | + { |
| 62 | + "server_side_key": "ser.your_server_side_key_1", |
| 63 | + "client_side_key": "your_client_side_key_1" |
| 64 | + } |
| 65 | + ], |
| 66 | + "api_poll_frequency_seconds": 5, |
| 67 | + "logging": { |
| 68 | + "log_level": "DEBUG", |
| 69 | + "log_format": "json" |
| 70 | + } |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +</details> |
| 75 | + |
| 76 | +<details> |
| 77 | + |
| 78 | +<summary>Environment variables</summary> |
| 79 | + |
| 80 | +```ruby |
| 81 | +ENVIRONMENT_KEY_PAIRS='[{"server_side_key":"ser.your_server_side_key_1","client_side_key":"your_client_side_key_1"}]' |
| 82 | +API_POLL_FREQUENCY_SECONDS=5 |
| 83 | +LOGGING='{"log_level":"DEBUG","log_format":"json"}' |
| 84 | +``` |
| 85 | + |
| 86 | +</details> |
| 87 | + |
| 88 | +### Basic settings |
| 89 | + |
| 90 | +#### `environment_key_pairs` |
| 91 | + |
| 92 | +Specifies which environments to poll environment documents for. Each environment requires a server-side key and its |
| 93 | +corresponding client-side key. |
| 94 | + |
| 95 | +```json |
| 96 | +"environment_key_pairs": [{ |
| 97 | + "server_side_key": "ser.your_server_side_key", |
| 98 | + "client_side_key": "your_client_side_environment_key" |
| 99 | +}] |
| 100 | +``` |
| 101 | + |
| 102 | +#### `api_url` |
| 103 | + |
| 104 | +If you are self-hosting Flagsmith, set this to your API URL: |
| 105 | + |
| 106 | +```json |
| 107 | +"api_url": "https://flagsmith.example.com/api/v1" |
| 108 | +``` |
| 109 | + |
| 110 | +#### `api_poll_frequency_seconds` |
| 111 | + |
| 112 | +How often to poll the Flagsmith API for changes, in seconds. Defaults to 10. |
| 113 | + |
| 114 | +```json |
| 115 | +"api_poll_frequency_seconds": 30 |
| 116 | +``` |
| 117 | + |
| 118 | +#### `api_poll_timeout_seconds` |
| 119 | + |
| 120 | +The request timeout when trying to retrieve new changes, in seconds. Defaults to 5. |
| 121 | + |
| 122 | +```json |
| 123 | +"api_poll_timeout_seconds": 1 |
| 124 | +``` |
12 | 125 |
|
13 | | -The main benefit to running server-side SDKs in [Local Evaluation Mode](/integrating-with-flagsmith/sdks/server-side#local-evaluation) is that you get the lowest possible latency. |
| 126 | +#### `allow_origins` |
14 | 127 |
|
15 | | -## How Does It Work |
| 128 | +Set a value for the `Access-Control-Allow-Origin` header. Defaults to `*`. |
16 | 129 |
|
17 | | -:::info |
| 130 | +```json |
| 131 | +"allow_origins": "https://www.example.com" |
| 132 | +``` |
| 133 | + |
| 134 | +### Endpoint caches |
| 135 | + |
| 136 | +#### `endpoint_caches` |
| 137 | + |
| 138 | +Enables a LRU cache per endpoint. |
| 139 | + |
| 140 | +Optionally, specify the LRU cache size with `cache_max_size`. Defaults to 128. |
18 | 141 |
|
19 | | -The Edge Proxy has the same [caveats as running our SDK in Local Evaluation mode.](/integrating-with-flagsmith/sdks/server-side#local-evaluation). |
| 142 | +```json |
| 143 | +"endpoint_caches": { |
| 144 | + "flags": { |
| 145 | + "use_cache": false |
| 146 | + }, |
| 147 | + "identities": { |
| 148 | + "use_cache": true, |
| 149 | + "cache_max_size": 1000, |
| 150 | + } |
| 151 | +} |
| 152 | +``` |
| 153 | + |
| 154 | +### Server settings |
20 | 155 |
|
21 | | -::: |
| 156 | +#### `server.proxy_headers` |
22 | 157 |
|
23 | | -You can think of the Edge Proxy as a copy of our Python Server-Side SDK, running in [Local Evaluation Mode](/integrating-with-flagsmith/sdks/server-side#local-evaluation), with an API interface that is compatible with the Flagsmith SDK API. |
| 158 | +When set to `true`, the Edge Proxy will use the `X-Forwarded-For` and `X-Forwarded-Proto` HTTP headers to determine |
| 159 | +client IP addresses. This is useful if the Edge Proxy is running behind a reverse proxy, and you want the |
| 160 | +[access logs](#logging.override) to show the real IP addresses of your clients. |
24 | 161 |
|
25 | | -The Edge Proxy runs as a lightweight Docker container. It connects to the Flagsmith API (either powered by us at api.flagsmith.com or self-hosted by you) to get environment flags and segment rules. You can then point the Flagsmith SDKs to your Edge Proxy; it implements all the current SDK endpoints. This means you can serve a very large number of requests close to your infrastructure and users, at very low latency. Check out the [architecture below](#architecture). |
| 162 | +By default, only the loopback address is trusted. This can be changed with the [`FORWARDED_ALLOW_IPS` environment |
| 163 | +variable](#environment-variables). |
26 | 164 |
|
27 | | -The Proxy also acts as a local cache, allowing you to make requests to the Proxy without hitting the core API. |
| 165 | +```json |
| 166 | +"server": { |
| 167 | + "proxy_headers": true |
| 168 | +} |
| 169 | +``` |
28 | 170 |
|
29 | | -## Performance |
| 171 | +``` |
| 172 | +SERVER_PROXY_HEADERS=true |
| 173 | +``` |
30 | 174 |
|
31 | | -The Edge Proxy can currently serve ~2,000 requests per second (RPS) at a mean latency of ~7ms on an M1 MacBook Pro with a simple set of flags. Working with more complex environments with many segment rules will bring this RPS number down. |
| 175 | +### Logging |
32 | 176 |
|
33 | | -It is stateless and hence close to perfectly scalable when deployed behind a load balancer. |
| 177 | +#### `logging.log_level` |
34 | 178 |
|
35 | | -## Managing Traits |
| 179 | +Choose a logging level from `"CRITICAL"`, `"ERROR"`, `"WARNING"`, `"INFO"`, `"DEBUG"`. Defaults to `"INFO"`. |
36 | 180 |
|
37 | | -There is one caveat with the Edge Proxy. Because it is entirely stateless, it is not able to persist trait data into any sort of data store. This means that you _have_ to provide the full complement of traits when requesting the flags for a particular identity. Our SDKs all provide relevant methods to achieve this. An example using `curl` would read as follows: |
| 181 | +```json |
| 182 | +"logging": {"log_level": "DEBUG"} |
| 183 | +``` |
| 184 | + |
| 185 | +#### `logging.log_format` |
| 186 | + |
| 187 | +Choose a logging format between `"generic"` and `"json"`. Defaults to `"generic"`. |
| 188 | + |
| 189 | +```json |
| 190 | +"logging": {"log_format": "json"} |
| 191 | +``` |
| 192 | + |
| 193 | +#### `logging.log_event_field_name` |
| 194 | + |
| 195 | +Set a name used for human-readable log entry field when logging events in JSON. Defaults to `"message"`. |
| 196 | + |
| 197 | +```json |
| 198 | +"logging": {"log_event_field_name": "event"} |
| 199 | +``` |
38 | 200 |
|
39 | | -```bash |
40 | | -curl -X "POST" "http://localhost:8000/api/v1/identities/?identifier=do_it_all_in_one_go_identity" \ |
41 | | - -H 'X-Environment-Key: n9fbf9h3v4fFgH3U3ngWhb' \ |
42 | | - -H 'Content-Type: application/json; charset=utf-8' \ |
43 | | - -d $'{ |
44 | | - "traits": [ |
45 | | - { |
46 | | - "trait_value": 123.5, |
47 | | - "trait_key": "my_trait_key" |
| 201 | +#### `logging.colour` |
| 202 | + |
| 203 | +Added in [2.13.0](https://github.com/Flagsmith/edge-proxy/releases/tag/v2.13.0). |
| 204 | + |
| 205 | +Set to `false` to disable coloured output. Useful when outputting the log to a file. |
| 206 | + |
| 207 | +#### `logging.override` |
| 208 | + |
| 209 | +Added in [2.13.0](https://github.com/Flagsmith/edge-proxy/releases/tag/v2.13.0). |
| 210 | + |
| 211 | +Accepts |
| 212 | +[Python-compatible logging settings](https://docs.python.org/3/library/logging.config.html#configuration-dictionary-schema) |
| 213 | +in JSON format. You're able to define custom formatters, handlers and logger configurations. For example, to log |
| 214 | +everything a file, one can set up own file handler and assign it to the root logger: |
| 215 | + |
| 216 | +```json |
| 217 | +"logging": { |
| 218 | + "override": { |
| 219 | + "handlers": { |
| 220 | + "file": { |
| 221 | + "level": "INFO", |
| 222 | + "class": "logging.FileHandler", |
| 223 | + "filename": "edge-proxy.log", |
| 224 | + "formatter": "json" |
| 225 | + } |
| 226 | + }, |
| 227 | + "loggers": { |
| 228 | + "": { |
| 229 | + "handlers": ["file"], |
| 230 | + "level": "INFO", |
| 231 | + "propagate": true |
| 232 | + } |
| 233 | + } |
| 234 | + } |
| 235 | +} |
| 236 | +``` |
| 237 | + |
| 238 | +Or, log access logs to file in generic format while logging everything else to stdout in JSON: |
| 239 | + |
| 240 | +```json |
| 241 | +"logging": { |
| 242 | + "override": { |
| 243 | + "handlers": { |
| 244 | + "file": { |
| 245 | + "level": "INFO", |
| 246 | + "class": "logging.FileHandler", |
| 247 | + "filename": "edge-proxy.log", |
| 248 | + "formatter": "generic" |
| 249 | + } |
48 | 250 | }, |
49 | | - { |
50 | | - "trait_value": true, |
51 | | - "trait_key": "my_other_key" |
| 251 | + "loggers": { |
| 252 | + "": { |
| 253 | + "handlers": ["default"], |
| 254 | + "level": "INFO" |
| 255 | + }, |
| 256 | + "uvicorn.access": { |
| 257 | + "handlers": ["file"], |
| 258 | + "level": "INFO", |
| 259 | + "propagate": false |
| 260 | + } |
52 | 261 | } |
53 | | - ], |
54 | | - "identifier": "do_it_all_in_one_go_identity" |
55 | | -}' |
| 262 | + } |
| 263 | +} |
56 | 264 | ``` |
57 | 265 |
|
58 | | -Note that the Edge Proxy will currently _not_ send the trait data back to the Core API. |
| 266 | +When adding logger configurations, you can use the `"default"` handler which writes to stdout and uses formatter |
| 267 | +specified by the [`"logging.log_format"`](#logginglog_format) setting. |
| 268 | + |
| 269 | +### Health checks |
| 270 | + |
| 271 | +The Edge Proxy exposes two health check endpoints: |
| 272 | + |
| 273 | +* `/proxy/health/liveness`: Always responds with a 200 status code. Use this health check to determine if the Edge |
| 274 | + Proxy is alive and able to respond to requests. |
| 275 | +* `/proxy/health/readiness`: Responds with a 200 status if the Edge Proxy was able to fetch all its configured |
| 276 | + environment documents within a configurable grace period. This allows the Edge Proxy to continue reporting as healthy |
| 277 | + even if the Flagsmith API is temporarily unavailable. This health check is also available at `/proxy/health`. |
59 | 278 |
|
60 | | -## Deployment and Configuration |
| 279 | +You should point your orchestration health checks to these endpoints. |
61 | 280 |
|
62 | | -Please see the [hosting documentation](/deployment-self-hosting/hosting-guides/docker). |
| 281 | +#### `health_check.environment_update_grace_period_seconds` |
63 | 282 |
|
64 | | -## Architecture |
| 283 | +Default: `30`. |
65 | 284 |
|
66 | | -The standard Flagsmith architecture: |
| 285 | +The number of seconds to allow per environment key pair before the environment data stored by the Edge Proxy is |
| 286 | +considered stale. |
| 287 | + |
| 288 | +When set to `null`, cached environment documents are never considered stale, and health checks will succeed if all |
| 289 | +environments were successfully fetched at some point since the Edge Proxy started. |
| 290 | + |
| 291 | +The effective grace period depends on how many environments the Edge Proxy is configured to serve. It can be calculated |
| 292 | +using the following pseudo-Python code: |
| 293 | + |
| 294 | +```python |
| 295 | +total_grace_period_seconds = api_poll_frequency + (environment_update_grace_period_seconds * len(environment_key_pairs)) |
| 296 | +if last_updated_all_environments_at < datetime.now() - timedelta(seconds=total_grace_period_seconds): |
| 297 | + # Data is stale |
| 298 | + return 500 |
| 299 | +# Data is not stale |
| 300 | +return 200 |
| 301 | +``` |
67 | 302 |
|
68 | | - |
| 303 | +### Environment variables |
69 | 304 |
|
70 | | -With the proxy added to the mix: |
| 305 | +Some Edge Proxy settings can only be set using environment variables: |
71 | 306 |
|
72 | | - |
| 307 | +- `WEB_CONCURRENCY` The number of [Uvicorn](https://www.uvicorn.org/) workers. Defaults to `1`, which is |
| 308 | + [recommended when running multiple Edge Proxy containers behind a load balancer](https://fastapi.tiangolo.com/deployment/docker/#one-load-balancer-multiple-worker-containers). |
| 309 | + If running on a single node, set this [based on your number of CPU cores and threads](https://docs.gunicorn.org/en/latest/design.html#how-many-workers). |
| 310 | +- `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, `NO_PROXY`: These variables let you configure an HTTP proxy that the |
| 311 | + Edge Proxy should use for all its outgoing HTTP requests. |
| 312 | + [Learn more](https://www.python-httpx.org/environment_variables) |
| 313 | +- `FORWARDED_ALLOW_IPS`: Which IPs to trust for determining client IP addresses when using the `proxy_headers` option. |
| 314 | + For more details, see the [Uvicorn documentation](https://www.uvicorn.org/settings/#http). |
0 commit comments