Migrate Docker-Compose to Fly.io

Hi All,

I’ve got a docker-compose which runs all our services. We run an app with users in 30+ countries. It’s Sept. 22, do we have a better support for docker-compose?

Trying to wrap my head around running each service in the docker-compose independently.

Please see my current docker-compose attached


.

  • nginx service serves as our reverse proxy
  • api service has 4 instances (nodejs, express, apollo-server e.t.c)
  • db service – using postgres image
  • redis-master service – using bitnami/redis image
  • redis-replica service – using bitnami/redis image
  • A bunch of environment variables attached via [env]: ./.env`

So I’m looking at the following workflow, please correct me if I’m mistaken:

  • Convert the docker-compose instance into separate dockerfiles and keep different
  • For the Node Express service i.e (api), according to my docker compose, I’ll have that in one dockerfile with its environment variables and a separate fly app.
  • For the nginx service, another separate fly app and its own dockerfile
  • I have AWS RDS instance which I can move over to fly postgres cluster to replace the db service and pass the connection string.
  • I have redis-master and redis-replica which I can move over to fly redis (though not sure how to setup the master/slave on fly)

Questions:

  • Are there any other recommendation to ensure this is a performant, yet cost effective setup?
  • Latency is a massive issue which is why we’re looking to migrate to fly.io. How can we scale our api(graphql) and postgresql instances to better serve our users?
  • Any thought on how to manage environment variables in fly.toml? Can I just copy the .env section in the docker-compose and add to fly.toml file instance of an app?
  • Regarding cost, looking at the description above, will the monthly cost likely to be significant? We are bootstrapping and paying for services out-of-pocket atm so every little help :slight_smile:
2 Likes

Hi,

Ref the recently announced managed Redis by Upstash (though, it has its trade-offs) or this DIY geo-replicated Redis.

One might choose to drop ngnix reverse-proxy altogether as Fly’s proxy primitives (though limited) are good enough in most cases. Personally, I go long ways just to fit my app within Fly’s primitives to get as close to zero-devops as possible.

I don’t use DBs on Fly, but I hear more db services are coming soon-ish. Otherwise, PlanetScale (MySQL) seems neat and cheap, too. There’s SQLite on LiteFS, if you’re adventurous.

If all of these need to run on the same IP (but on different ports), then one may choose to run them as a Fly multi-process app. As a multi-process Fly app, if one may want to use the same ports and IPs (and handle request routing in-app themselves). If not, one can run them in their own Fly app, each behind a different IP/port.

You’ll have to model this one yourself, I’m afraid. Fly Machine apps is cheapest option but it does have rough edges, though, admittedly it is getting better all the time.

Fly won’t solve all latency issues, but it would solve a bunch of other things that will let you spend time on solving your business problems, I am sure :slight_smile:


Are there any other recommendation to ensure this is a performant, yet cost effective setup?

Fly does have a lower TCO (ex) than most Cloud providers (incl scaleway, lightsail, digitalocean, vultr etc), but at the end of the day, it really depends on how you set your Fly apps up.

I don’t run DBs on Fly (I prefer fully managed providers), and so tagging @greg / @charsleysa / @tj1 for their inputs.

From what I recall, env vars one sets in the dockerfile or via the dockerfile take precedence over the env vars set via the flyctl deploy -e switch and/or those set via fly.toml. The env vars set by Fly (runtime) secrets take precedence over all other env vars, no matter how they’re set.

4 Likes

Thanks @ignoramous for your kind response. You’re amazing! :star_struck:

Ref the recently announced managed Redis by Upstash (though, it has its trade-offs) or this DIY geo-replicated Redis.

Yep, was going to leverage this. Good you mentioned it!

One might choose to drop ngnix reverse-proxy altogether as Fly’s proxy primitives (though limited) are good enough in most cases. Personally, I go long ways just to fit my app within Fly’s primitives to get as close to zero-devops as possible.

This is interesting! Any documentation you can point me to when setting this up?

I don’t use DBs on Fly, but I hear more db services are coming [soon-ish]…

You reckon Postgres Clusters may suffice in this instance? Have you seen any insufficiency around it?

If all of these need to run on the same IP (but on different ports), then one may choose to run them as a Fly multi-process app. As a multi-process Fly app, if one may want to use the same ports…

I’m glad I raised the question in the first place! I’ll definitely look in this direction. Considering my limited DevOps experience hence why I raised the question. In terms of setup, and looking at your notes above, I intend to:

  • Run Node/Express/Apollo instances on fly multi-processmay come back with additional question on getting the difference process to communicate
  • Run Managed-redis instance via fly redis create ...
  • Run Postgres Clusters for DBs and tune?
  • Any pointers/best-practices as per GraphQL/Apollo and Redis?

Any additional pointers @greg / @charsleysa / @tj1 could provide will be super helpful.

I appreciate you all!

Hey,

Well @ignoramous seems to have done a thorough job so not sure what else I can add beyond their answer! From your original post:

do we have a better support for docker-compose

… the answer there is no support for docker-compose. As @ignoramous says, to migrate you need to adapt to do it Fly’s way (because Fly does not actually use docker at all, but that’s another story).

As regards the database, indeed Fly does provide Postgres. Disadvantages? It’s not quite as complete as e.g RDS (which you would be migrating from). Backups, restoration etc. Latency is probably comparable given it’s likely to be in a similar data centre behind the scenes. Upside? A bigger consideration would probably be that if your app is on Fly but your database remains in AWS, you would either have to open your RDS database up to any IP (because Fly don’t provide an external IP you can whitelist in AWS) which isn’t ideal or (I guess) set up some proxy to it, which is an additional faff. No doubt someone on here has connected their Fly app to their AWS database so there is probably some post on this forum explaining how they solved it. If your database and app are both within Fly’s network, you don’t have that issue, naturally. They provide a free, private, encrypted network apps within the same organisation can communicate on (that’s those name.internal hostnames). So there is no issue about firewall/IP then.

2 Likes

There’s undocumented stuff, but here’s a wall of things you can do as far as networking is concerned: fly-replay, fly-prefer-region, regional-ips, fly-force-instance-id, 6pn-only apps, 6pn with geo-DNS, 6pn with Anycast, 6pn namespaces, anchor-scaling, instance discovery, configurable - http/tls/proxy-proto handlers, 3x health-checks, Fly-managed certs and a few other things.

Fly maintains example apps up here: Fly.io Apps · GitHub

I wouldn’t know. But really, I’d use PlanetScale, Neon.Tech, Aurora Serverless v2 or some such if it were up to me.

Fly multi-process apps are still in preview (and may or may not suit your setup, hard to tell). We, personally, avoid multi-process apps but want to use them once they get better (esp, with proper request-routing and Machine apps support).

Wrong person, but others have been running these and there are docs. More specifically, some of the framework docs are excellent (but I take you’re not using Rails or Phoenix or Laravel that seems to be the area of extreme focus for Fly) I’ve been tempted by Upstash Redis, but haven’t gotten to code it up and put it to proper use.

You’re bootstrapping, or else:wink:

1 Like

After @ignoramous and @greg 's responses, not too much to add.

Regarding performance and cost, this really depends on quite a few factors but AWS on-demand instances are pretty pricey.

  1. I would be careful with multi-process though, the last time I checked autoscaling wasn’t working well with that. Best to use a service manager like runit to supervise all the processes instead.
  2. Autoscaling works based on the soft-limit variable in fly.toml and once it hits the hardlimit, no more requests will go to that instance and instead will be routed to another VM which will increase the overall latency. Where the host actually is placed when scaling is based on quite a few variables that we don’t have access to (capacity, etc) so it may appear quite random. Tuning this is quite important.
  3. If your application is highly database dependent, than minimizing the number of database calls will be very important. For instance, in my migration I had the latency down to sub-50 ms on page updates that did not hit the database. However when the user was logged in, there was a single query that hit the db which caused the pages to be 200+ms instead. Took me forever to track that down.
  4. Not sure how many regions you’re planning for, but I’d go with a smaller region count to start with for the data layer. And if you’re on RDS already, perhaps it is best to stay there for now as Fly has no managed databases. I’ve already got my application working with sqlite and will be migrating once litefs is ready to reduce the operational burden.
2 Likes