Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app-guides/6pndemochat.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ let natsapphost=process.env.NATSAPPHOST
const nc = NATS.connect(natsapphost+".internal");
```

The combination of an app name and `.internal` in 6PN networking resolves to the IPv6 addresses of the first instance of all instances if that app currently running. So, if we set NATSAPP to `myclusterapp` then scaling and adding nodes to `myclusterapp` would automatically be reflected in `myclusterapp.internal`. 6PN has a number of `.internal` addresses an app can query which you can read about in the [6PN documentation](/networking/services#internal-addresses).
The combination of an app name and `.internal` in 6PN networking resolves to the IPv6 addresses of the first instance of all instances if that app currently running. So, if we set NATSAPP to `myclusterapp` then scaling and adding nodes to `myclusterapp` would automatically be reflected in `myclusterapp.internal`. 6PN has a number of `.internal` addresses an app can query which you can read about in the [6PN documentation](/networking/private-networking#fly-io-internal-dns).

Once the chat server has connected to the NATS cluster, it subscribes to a topic `msg` and any message written to `msg` will be broadcast to all web socket clients that are connected. If any of those web socket clients sends a message to the chat server, then it will publish that message to the `msg` topic on the NATs cluster. It doesn't even try to echo it locally, relying on the NATS cluster to publish to subscribers.

Expand Down
2 changes: 1 addition & 1 deletion app-guides/grpc-and-grpc-web-services.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ You'll notice that the clock stops streaming after exactly 10 seconds – this i

While gRPC works really well when communicating between servers on the same rack or same datacenter, the principles it uses apply equally well to clients on websites or mobile devices. The HTTP/2 communication channel remains open as much as possible to provide a low-latency and low-energy way to speak to your server, and the underlying [Protocol Buffers](https://developers.google.com/protocol-buffers) serialization/de-serialization system is very low-overhead, so it's great for cellular or metered-bandwidth connections.

Running your backend services on Fly lets you put your services really close to every user in your global client base. If your data is in a central location, your services can use the local Redis cache that Fly provides at each edge location to cache data, and broadcast changes or invalidations via the [global Redis command broadcast](/upstash/redis#managing-redis-data-globally). There are also data solutions from other cloud providers like [DynamoDB Global Tables](https://aws.amazon.com/dynamodb/global-tables/) (NoSQL), [Aurora Multi-Master](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-global-database.html) (SQL) or [Google Cloud Spanner](https://cloud.google.com/spanner) (SQL) that can help spread your data across the world and closer to your application servers.
Running your backend services on Fly lets you put your services really close to every user in your global client base. If your data is in a central location, your services can use the local Redis cache that Fly provides at each edge location to cache data, and broadcast changes or invalidations via the [global Redis command broadcast](/upstash/redis). There are also data solutions from other cloud providers like [DynamoDB Global Tables](https://aws.amazon.com/dynamodb/global-tables/) (NoSQL), [Aurora Multi-Master](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-global-database.html) (SQL) or [Google Cloud Spanner](https://cloud.google.com/spanner) (SQL) that can help spread your data across the world and closer to your application servers.

The reduced latency from having your servers close to your clients is especially useful in gRPC where you want all your calls to finish as quickly as possible. If your clients are mobile applications, they'll benefit even more from the latency reduction when they're running on cellular networks, where bandwidth is low and re-connections are common.

Expand Down
6 changes: 3 additions & 3 deletions app-guides/openresty-nginx-plus-lua.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@

# Running OpenResty on Fly
{/* cut here */}
If you've been developing web applications for long, you've probably heard of [Nginx](https://nginx.org/en/). It's a widely used open-source HTTP server, but it has some limitations. By default, Nginx doesn't provide a way to program logical operators or write custom logic. Developers can circumvent this by using modules like [Nginx JavaScript](https://www.nginx.com/blog/introduction-nginscript/) or [Lua](https://github.com/openresty/lua-nginx-module), but that takes extra work to install and configure.

Check warning on line 12 in app-guides/openresty-nginx-plus-lua.mdx

View workflow job for this annotation

GitHub Actions / Vale linter

[vale] reported by reviewdog 🐶 Consider using 'open source' instead of 'open-source'. Raw Output: {"message":"Consider using 'open source' instead of 'open-source'.","location":{"path":"app-guides/openresty-nginx-plus-lua.mdx","range":{"start":{"line":12,"column":130},"end":{"line":12,"column":141}}},"severity":"INFO","code":{"value":"Fly.WordList"}}

[OpenResty](https://openresty.org/en/) allows you to build full-fledged web applications by bundling Nginx with a Lua compiler and several common modules. This makes OpenResty more broadly useful than vanilla Nginx, but depending on your use case, it could be overkill.

For example, if you want to [run a simple reverse proxy](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/) on your server, Nginx can handle it. If you need to add rate limiting, authentication, advanced caching, or a connection to a database, you'll need a solution like OpenResty. With the increase in distributed computing and microservices, OpenResty has become an excellent option for complex firewalls, API gateways, and even full-fledged web applications.

Check warning on line 16 in app-guides/openresty-nginx-plus-lua.mdx

View workflow job for this annotation

GitHub Actions / Vale linter

[vale] reported by reviewdog 🐶 Consider using 'firewall rules' instead of 'firewalls'. Raw Output: {"message":"Consider using 'firewall rules' instead of 'firewalls'.","location":{"path":"app-guides/openresty-nginx-plus-lua.mdx","range":{"start":{"line":16,"column":416},"end":{"line":16,"column":425}}},"severity":"INFO","code":{"value":"Fly.WordList"}}

Typically, OpenResty is deployed to a central server. Web requests go through OpenResty before being routed to the relevant backing services, but this model isn't great for performance or redundancy. While you can set up several servers and run OpenResty on each, you have to figure out how to maintain and sync data across them.

Expand All @@ -36,7 +36,7 @@
```

### Configuring the Dockerfile
Fly will build and run your Docker image in a [Firecracker VM](/reference/architecture#microvms) on the edge, but you need to select and configure a Dockerfile first.
Fly will build and run your Docker image in a [Firecracker VM](/reference/architecture#compute) on the edge, but you need to select and configure a Dockerfile first.

OpenResty provides [several Docker images](https://github.com/openresty/docker-openresty) you can use for your application. I opted for Centos, but because of [an apparent bug in their Docker image](https://github.com/openresty/docker-openresty/issues/124), specified the `1.15.8.1-4-centos` tag.

Expand Down Expand Up @@ -195,16 +195,16 @@
}
```

This configuration passes your request through using the `proxy_pass` directive at the end, as the previous version did. Before it does, it checks if the visitor has reached their request limit using the `Fly-Client-IP` header [attached to the request by Fly](/networking/services#http). If the IP address has called the endpoint in the past 2 seconds, it returns a 503 response and logs the error.
This configuration passes your request through using the `proxy_pass` directive at the end, as the previous version did. Before it does, it checks if the visitor has reached their request limit using the `Fly-Client-IP` header [attached to the request by Fly](/networking/services#http-handler). If the IP address has called the endpoint in the past 2 seconds, it returns a 503 response and logs the error.

You can re-deploy this configuration file to Fly (again using `flyctl deploy`) and call the endpoint twice in quick succession to test it.

You've now got a working rate limiter, but you're still not finished. In the last section, you'll see how to connect your OpenResty application to Redis to store API keys that can be used for authentication.

### Checking API Keys Against a Redis Store
Fly offers a [region-local Redis instance](/upstash/redis) to all deployments, which can be used to persist data for longer periods. While this volatile datastore is not meant for permanent use, you can use it to cache data so that it's accessible on the edge.

Check warning on line 205 in app-guides/openresty-nginx-plus-lua.mdx

View workflow job for this annotation

GitHub Actions / Vale linter

[vale] reported by reviewdog 🐶 Consider using 'data store' instead of 'datastore'. Raw Output: {"message":"Consider using 'data store' instead of 'datastore'.","location":{"path":"app-guides/openresty-nginx-plus-lua.mdx","range":{"start":{"line":205,"column":154},"end":{"line":205,"column":163}}},"severity":"INFO","code":{"value":"Fly.WordList"}}

In this last step, you'll connect to the Fly Redis instance using the OpenResty [Redis driver](https://github.com/openresty/lua-resty-redis). You'll authenticate requests using a `key` passed in by the user through a query string argument and return a 401 response code if authentication fails. In a real application, you would probably push data to the Fly Redis instance using [their global data store](/upstash/redis#managing-redis-data-globally), but because this is a demonstration app, you'll hard code a few sample API keys.
In this last step, you'll connect to the Fly Redis instance using the OpenResty [Redis driver](https://github.com/openresty/lua-resty-redis). You'll authenticate requests using a `key` passed in by the user through a query string argument and return a 401 response code if authentication fails. In a real application, you would probably push data to the Fly Redis instance using [their global data store](/upstash/redis), but because this is a demonstration app, you'll hard code a few sample API keys.

First, write a Lua script to parse the connection string. Fly's `FLY_REDIS_CACHE_URL` must be split at the `:` and `@` characters, so you can write a function that takes any number of characters as possible delimiters ([credit to Walt Howard on Stack Overflow](https://stackoverflow.com/a/29497100/977192) for this one). Create a new file called `split.lua` and add the following:

Expand Down Expand Up @@ -294,6 +294,6 @@
Deploy this updated configuration file using `flyctl deploy` and visit `https://<your-app-name>.fly.dev/api/` again. This time, you will get a 401 response from OpenResty. Add one of the API keys you hard-coded as a `key` in the query string: `https://<your-app-name>.fly.dev/api/?key=AstIqxOHpyAToCwh8qeL` and you'll see the JSON placeholder data again.

## Conclusion
In this post, you've seen how to create an OpenResty application to extend Nginx's functionality. You've added rate-limiting to ensure that users don't abuse your API and simple authentication using data cached in Redis. Finally, by deploying the application on Fly, you can take advantage of their globally distributed edge hosting environment to make your app faster and more reliable than it would be on traditional hosting.

Check warning on line 297 in app-guides/openresty-nginx-plus-lua.mdx

View workflow job for this annotation

GitHub Actions / Vale linter

[vale] reported by reviewdog 🐶 Consider using 'capability' or 'feature' instead of 'functionality'. Raw Output: {"message":"Consider using 'capability' or 'feature' instead of 'functionality'.","location":{"path":"app-guides/openresty-nginx-plus-lua.mdx","range":{"start":{"line":297,"column":84},"end":{"line":297,"column":97}}},"severity":"INFO","code":{"value":"Fly.WordList"}}

If you have any questions about using OpenResty with [Fly.io](https://fly.io/), be sure to reach out so we can help you get started.
2 changes: 1 addition & 1 deletion app-guides/puppeteer-js-renderer.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ To show the YouTube video views count, a small scraper written with [Axrio](http

The `yt-views.js` is a basic scraper which performs a GET request for the given YouTube URL with Puppeteer. This will return the final DOM after the page's JavaScript executes. Then it parses the title and views count out of the rendered page's markup and prints it on the console.

It uses the puppeteer-js-renderer service that is already deployed and running on Fly.io. Have a look at he "[how to use it as a service](#how-to-use-it-as-a-service)" section for more information. To start your own instance on Fly.io jump to the "[how to deploy on fly.io](#how-to-deploy-it-on-flyio)" section.
It uses the puppeteer-js-renderer service that is already deployed and running on Fly.io. Have a look at he "[how to use it as a service](#how-to-use-it-as-a-service)" section for more information. To start your own instance on Fly.io jump to the "[how to deploy on fly.io](#how-to-deploy-it-on-fly-io)" section.

## Run locally

Expand Down
2 changes: 1 addition & 1 deletion app-guides/run-a-global-image-service.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ If you've already installed `flyctl`, move on to the next step. If not, hop over

### Sign In (or Up) for Fly

If you have a Fly account, all you need to do is [sign-in with `flyctl`](#returning-user).
If you have a Fly account, all you need to do is [sign-in with `flyctl`](#sign-in-or-up-for-fly).

##### New User?

Expand Down
2 changes: 1 addition & 1 deletion app-guides/run-a-private-dns-over-https-service.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@
531743b7098c: Pushed
deployment-1580379849: digest: sha256:220099d5aae94190a206bfdda219480ffbf8da988690361ae6d1d280732d8a68 size: 1158
--> done
==> Creating Release

Check warning on line 245 in app-guides/run-a-private-dns-over-https-service.mdx

View workflow job for this annotation

GitHub Actions / Vale linter

[vale] reported by reviewdog 🐶 'Release' is repeated! Raw Output: {"message":"'Release' is repeated!","location":{"path":"app-guides/run-a-private-dns-over-https-service.mdx","range":{"start":{"line":245,"column":14},"end":{"line":245,"column":22}}},"severity":"WARNING","code":{"value":"Vale.Repetition"}}
Release v0 created
==> Monitoring Deployment
You can detach the terminal anytime without stopping the deployment
Expand Down Expand Up @@ -296,4 +296,4 @@

### Handle your own TLS

Fly can take care of TLS + HTTPS for you, but it doesn't have to. If you'd prefer to terminate your own TLS, you can generate your own certificates, store them as encrypted Fly secrets, and tweak [the Dockerfile](#write-a-custom-dockerfile) to pull them from your app environment. We'll happily forward encrypted TCP to your app and let you worry about the rest.
Fly can take care of TLS + HTTPS for you, but it doesn't have to. If you'd prefer to terminate your own TLS, you can generate your own certificates, store them as encrypted Fly secrets, and tweak [the Dockerfile](#1-write-a-custom-dockerfile) to pull them from your app environment. We'll happily forward encrypted TCP to your app and let you worry about the rest.
2 changes: 1 addition & 1 deletion apps/app-availability.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ If you're deploying Fly Postgres, our un-managed database app, then you need to

When you use [Fly Volumes](/volumes) with your app, your app needs to implement replication and backups. Volumes are block devices that live on a single disk array and get mounted directly by your Machine. There's no magic. And there's no built-in replication between volumes. If your app provides a clustering mechanism with data replication, like most databases do, we recommend you take advantage of that and run multiple instances with attached volumes. When possible, we place your app's volumes in different hardware zones within a region to mitigate hardware failures.

This brings us to disaster recovery and being prepared. You should have backups of your data and understand how to recover that data if needed. Fly.io takes daily snapshots of volumes and stores them for 5 days by default. You can [restore a volume's data](/volumes/volume-manage#restore-a-volume-from-a-snapshot) from one of these daily snapshots. If your use case requires more frequent backups, then you'll need to set up tooling and processes to backup your volumes on the required schedule.
This brings us to disaster recovery and being prepared. You should have backups of your data and understand how to recover that data if needed. Fly.io takes daily snapshots of volumes and stores them for 5 days by default. You can [restore a volume's data](/volumes/snapshots#restore-a-volume-from-a-snapshot) from one of these daily snapshots. If your use case requires more frequent backups, then you'll need to set up tooling and processes to backup your volumes on the required schedule.

## Fly.io features for app resiliency

Expand Down
2 changes: 1 addition & 1 deletion blueprints/cell-based.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ fly machines list -q
In order to prepare for a multiple Machine deployment, we are going to need to
ensure that each request is routed to the correct Machine. We can accomplish
this via middleware that makes use of the
[fly-replay](/networking/dynamic-request-routing#the-fly-replay-response-header)
[fly-replay](/networking/dynamic-request-routing#how-fly-replay-works)
response header.

Place the following into `config/initializers/fly_router.rb`:
Expand Down
4 changes: 2 additions & 2 deletions blueprints/connecting-to-user-machines.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ The [private-network-router](https://github.com/fly-apps/private-network-router)

1. Receives requests on any port
2. Determines the target app and machine based on the request
3. Sends a `fly-replay` response header with parameters like `app` and `instance` to route the request to the correct app and machine. For more details on the parameters, see the [fly-replay documentation](/networking/dynamic-request-routing#the-fly-replay-response-header).
3. Sends a `fly-replay` response header with parameters like `app` and `instance` to route the request to the correct app and machine. For more details on the parameters, see the [fly-replay documentation](/networking/dynamic-request-routing#how-fly-replay-works).

This pattern allows you to:
- Keep management APIs private by running them with flycast
Expand All @@ -68,7 +68,7 @@ Validation of replayed requests should happen within the user machine. We recomm

1. **Create a preshared key for each user app** and set it as an app secret.
2. When issuing a replay, set the header: `fly-replay: app=<app>,state=<key>`.
3. On the user machine, parse the `fly-replay-src` header. This header contains fields such as `instance`, `region`, `t`, and `state` (see [fly-replay documentation](/networking/dynamic-request-routing#the-fly-replay-response-header)).
3. On the user machine, parse the `fly-replay-src` header. This header contains fields such as `instance`, `region`, `t`, and `state` (see [fly-replay documentation](/networking/dynamic-request-routing#how-fly-replay-works)).
4. Check the `state` value in `fly-replay-src` against the preshared key for the app. If it matches, the request is validated.

See the [TypeScript example below](#example-authenticating-a-replayed-request-in-typescript) for a practical implementation of this validation.
Expand Down
2 changes: 1 addition & 1 deletion blueprints/opensshd.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ copying files ([rsync](https://rsync.samba.org/), [scp](https://en.wikipedia.org
editing ([emacs](https://www.gnu.org/software/emacs/manual/html_node/emacs/Remote-Files.html), [vim](https://www.vim.org/scripts/script.php?script_id=1075), [vscode](https://code.visualstudio.com/docs/remote/ssh)), and
deployment ([ansible](https://www.ansible.com/), [github actions](https://github.com/marketplace/actions/ssh-deploy), and [kamal](https://kamal-deploy.org/)).

One way to use these tools is to [set up a wireguard VPN](/networking/private-networking#install-your-wireguard-app) and
One way to use these tools is to [set up a wireguard VPN](/networking/private-networking#1-install-your-wireguard-app) and
[issue a new SSH credential](/flyctl/cmd/fly_ssh_issue). This may be impractical for some use cases (example: github actions).

As an alternative, you can configure and deploy an SSH server on your machine(s). This guide will walk you through the process.
Expand Down
2 changes: 1 addition & 1 deletion blueprints/per-user-dev-environments.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
Creating apps dynamically for each user at request time can be slow. To ensure fast provisioning:

- **Pre-create** a pool of Fly apps and machines ahead of time (using the [Fly Machines API or CLI](/apps/overview)).
- Store app details (e.g., app_name: `alice-123`) in a datastore accessible to your router app.

Check warning on line 45 in blueprints/per-user-dev-environments.mdx

View workflow job for this annotation

GitHub Actions / Vale linter

[vale] reported by reviewdog 🐶 Consider using 'data store' instead of 'datastore'. Raw Output: {"message":"Consider using 'data store' instead of 'datastore'.","location":{"path":"blueprints/per-user-dev-environments.mdx","range":{"start":{"line":45,"column":56},"end":{"line":45,"column":65}}},"severity":"INFO","code":{"value":"Fly.WordList"}}
- Assign apps to users at provisioning time.

**Fly Machines**
Expand All @@ -55,7 +55,7 @@

## Pointers & Footguns {#pointers-amp-footguns}

- **Machines & volumes are tied to physical hardware:** hardware failures can destroy machines and attached volumes. **Always persist important user data** (code, config, outputs) to external storage (like [Tigris Data](/tigris#main-content-start) or AWS S3).
- **Machines & volumes are tied to physical hardware:** hardware failures can destroy machines and attached volumes. **Always persist important user data** (code, config, outputs) to external storage (like [Tigris Data](/tigris) or AWS S3).
- **Your users will break their environments:** pre-create standby machines to handle hardware & runtime failures, or the inevitable user or robot poisoned environment. Pre-create standby machines that you can quickly activate in these scenarios.
- **Machine restarts reset ephemeral filesystem:** the temporary Fly Machine filesystem state resets on Machine restarts, ensuring clean environments. However, volume data remains persistent, making it useful for retaining user progress or state.
- **One app per user:** Putting all user Machines into a single app and routing to them with dynamic routing works, but `auto_stop` won't behave the way you expect once you scale. The Fly Proxy's stop loop is rate-limited: it [stops or suspends at most one Machine per region each pass, and runs every few minutes](/reference/fly-proxy-autostop-autostart#fly-proxy-process-to-stop-or-suspend-machines). That's fine for a normal app; with thousands of Machines the loop can't keep up, and most of your idle Machines stay running. Machines in a single app also share app-level secrets and a flat [private network](/networking/private-networking). A compromised user environment can reach every other Machine in the app. If you do keep all user Machines in one app, implement stop-when-idle behavior in your app or orchestrator (see [apps that shut down when idle](/launch/autostop-autostart#apps-that-shut-down-when-idle)). Don't rely on the Fly Proxy to keep most Machines stopped.
Expand Down
4 changes: 2 additions & 2 deletions blueprints/shared-nothing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@

The first technique is to [listen](https://github.com/rubys/showcase/blob/main/app/javascript/controllers/region_controller.js) for
[turbo:before-fetch-requests](https://turbo.hotwired.dev/reference/events#http-requests) events and
insert a [fly-prefer-region header](/networking/dynamic-request-routing#the-fly-prefer-region-request-header).
insert a [fly-prefer-region header](/networking/dynamic-request-routing#the-fly-prefer-region-header).
The region is extracted from a data attribute added to the [body](https://github.com/rubys/showcase/blob/aae08a6d57f92335b2cdbb94756e5416b7b50f83/app/views/layouts/application.html.erb#L16) tag.

This all made possible by how [Turbo works](https://turbo.hotwired.dev/handbook/introduction#turbo-drive%3A-navigate-within-a-persistent-process):
Expand All @@ -158,7 +158,7 @@

This replays action cable (web socket) requests to the correct region, and
[reverse proxies](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/)
all other requests to the correct machine using its [`.internal` address](/networking/private-networking#fly-io-internal-addresses). The latter is done because [fly-replay](/networking/dynamic-request-routing#limitations)
all other requests to the correct machine using its [`.internal` address](/networking/private-networking#fly-io-internal-dns). The latter is done because [fly-replay](/networking/dynamic-request-routing#requirements-and-limitations)
is limited to 1MB requests, and uploading audio files may exceed this limit.

## Appliances
Expand Down Expand Up @@ -279,7 +279,7 @@
so your users see something while they are waiting.
</Info>

If you go to https://smooth.fly.dev/ the server nearest to you will need to spin up the index application. And if, from there, you click on demo, a second application will be started. While you might get lucky and somebody else (or a bot) may have already started these for you, the normal case is that you will have to wait.

Check warning on line 282 in blueprints/shared-nothing.mdx

View workflow job for this annotation

GitHub Actions / Vale linter

[vale] reported by reviewdog 🐶 Consider using 'click' or 'click in' instead of 'click on'. Raw Output: {"message":"Consider using 'click' or 'click in' instead of 'click on'.","location":{"path":"blueprints/shared-nothing.mdx","range":{"start":{"line":282,"column":132},"end":{"line":282,"column":140}}},"severity":"INFO","code":{"value":"Fly.WordList"}}

Since all the services that are needed to support this application are on one machine, `fly deploy` can't simply start a new machine, wait for it to start, direct traffic to it, and only then start dismantling the previous machine. Instead, each machine needs to be taken offline, have the rootfs replaced with a new image, and all of its services restarted. Once that is complete, migrations need to be run on that machine; and in my case I may have several SQLite databases that need to be upgraded. I also have each machine that comes up rsync with the primary region. And finally, I generate an operational NGINX configuration and run [nginx reload](https://docs.nginx.com/nginx/admin-guide/basic-functionality/runtime-control/).

Expand All @@ -299,7 +299,7 @@
command.
</Info>

Now lets look at what happens when I create a new event, in a new region. I (a human) get an email, I bring up my admin UI (on an instance of the index app I mentioned above). I fill out a form for the new event. I go to a separate page and add a new region. I then go to a third form where I can review and apply the changes. This involves regenerating my map, and I need to update the NGINX configuration in every existing region. There are shortcuts I can utilize which will update files on each machine and reload the NGINX configuration, but I went that way I would need to ensure that the end result exactly matches the reproducible result that a subsequent `fly deploy` would produce. I don't need that headache, so I'm actually using `fly deploy` which can add tens of seconds to the actual deploy time.

Check warning on line 302 in blueprints/shared-nothing.mdx

View workflow job for this annotation

GitHub Actions / Vale linter

[vale] reported by reviewdog 🐶 Consider using 'use' instead of 'utilize'. Raw Output: {"message":"Consider using 'use' instead of 'utilize'.","location":{"path":"blueprints/shared-nothing.mdx","range":{"start":{"line":302,"column":466},"end":{"line":302,"column":473}}},"severity":"INFO","code":{"value":"Fly.WordList"}}

I started out running the various commands manually, but that grew old fast. So eventually
I invested in creating an admin UI.
Expand Down Expand Up @@ -437,5 +437,5 @@
## Related reading

- [Getting Started with N‑Tier Architecture](/blueprints/n-tier-architecture) A contrasting architecture pattern where state is shared across the data tier — useful for understanding when shared‑nothing *isn’t* the best fit.
- [Session Affinity (a.k.a. Sticky Sessions)](/blueprints/sticky-sessions) Focused on routing and state‑isolation patterns that often come up when you adopt a shared‑nothing approach and need to keep state local to a machine.

Check warning on line 440 in blueprints/shared-nothing.mdx

View workflow job for this annotation

GitHub Actions / Vale linter

[vale] reported by reviewdog 🐶 Consider using 'or' or 'also known as' instead of 'a.k.a'. Raw Output: {"message":"Consider using 'or' or 'also known as' instead of 'a.k.a'.","location":{"path":"blueprints/shared-nothing.mdx","range":{"start":{"line":440,"column":22},"end":{"line":440,"column":27}}},"severity":"INFO","code":{"value":"Fly.WordList"}}
- [Networking](/networking) The comprehensive networking overview (private networks, Anycast, WireGuard tunnels, routing) — helpful when building globally distributed isolated nodes in a shared‐nothing architecture.
2 changes: 1 addition & 1 deletion blueprints/sticky-sessions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ This code will subscribe and unsubscribe from

## Fly-Replay

This approach is implemented entirely on the server. Your application examines each request, determines which Machine should handle it, and returns a `fly-replay` response header to route the request accordingly. Initial requests will require an additional "hop" to route, and [requests are limited to payloads of 1 megabyte](/networking/dynamic-request-routing#limitations).
This approach is implemented entirely on the server. Your application examines each request, determines which Machine should handle it, and returns a `fly-replay` response header to route the request accordingly. Initial requests will require an additional "hop" to route, and [requests are limited to payloads of 1 megabyte](/networking/dynamic-request-routing#requirements-and-limitations).

The example below creates [Express Middleware](https://expressjs.com/en/guide/using-middleware.html) that routes requests based on a session cookie:

Expand Down
2 changes: 1 addition & 1 deletion deep-dive/tigris.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ source_url: https://fly.io/docs/deep-dive/tigris/

Unlike relational databases, there may be reasons why you want to make your object store available via the internet. In this demo, the object store starts out private, but you can make it [public](/tigris#public-buckets) if you want. Public buckets serve objects without authentication at `https://bucket-name.t3.tigrisfiles.io/key-name`.

And if you happen to have an existing S3 object store, check out [shadow buckets](/tigris#migrating-to-tigris-with-shadow-buckets), which enable you to incrementally migrate your data.
And if you happen to have an existing S3 object store, check out [shadow buckets](/tigris#migrate-from-another-s3-compatible-provider-with-zero-downtime), which enable you to incrementally migrate your data.
Loading
Loading