Yeah, the GraphQL API isn’t designed to programmatically launch apps/VMs. We have a Machines API specifically for this.
You don’t need to create and remove apps, necessarily. You can create/start/stop/destroy individual Machines with the Machines API.
What are you using the VMs for? We can give you some ideas about how best to use the API. GraphQL creates a lot of moving infrastructure, Machines are much simpler and more reliable for this kind of work.
In short, I have a container that hosts websocket server.
What I am doing at the moment is, I have a websocket server hosted outside of Fly (I need this anyway), that accepts connections, and for every accepted connection it spins up a new Fly app. This is because I need to reserve an exclusive container instance for the duration of the websocket connection.
If there was a lower level way to achieve the same, I could do that.
Using machines, is there a way to get a unique IP address (path, port, or another method) for that machine that I could use for the duration of the machine?
Ah! We have good support for that kind of workload with Machines, but it’s a big change from what you’re doing now.
The best way to do this on Fly.io is to create a router app that accepts all requests, then create a bunch of machines in a separate app. These machines should have a service with hard_limit=1 set for concurrency, and they should exit when they’re done with a request.
When you get a request to the router app, you can do whatever logic you need to authenticate it, then use the fly-replay header to reroute the request/websocket to one of your worker machines: The Fly-Replay Header · Fly Docs
Machines reset their state when they stop, so it’s safe to send different users to them between stop/start. Replay handles websockets just fine (we use this ourselves). The hard_limit ensures that only one websocket can be attached to a single machine at any given time. The important thing here is that the machine exits by itself when the websocket ends, otherwise it won’t clean up.
Part of the problem you’re running into now is that the load balanced IPs we give to apps are relatively complex. So you’re having to wait on coordination, the IP to propagate to our edges, etc. If you are up for making this work with Machines + Replay I think it’ll be a lot more reliable.
Our router actually starts a stopped machine if it needs to, so you can basically just replay requests and let us handle the rest (once a machine exists).
Being able to inspect traffic and inject messages makes my application design quite a bit simpler. However, if your design is noticeable more efficient, then (with quite a bit of work lol) I can move that logic to the individual nodes have them talk with the router when needed.
If yes, then why does it ask for an image parameter as opposed to inheriting it from the app?
Putting the above aside, I would expect that if hard_limit=1 that Fly creates a new machine for every request. Though, given that app configuration does not mention anything about machine types, it is not clear how would it know what machine type to provision. Furthermore, even when I have a dozen open sockets, I don’t see anything under the fly machines list --app ....
After a bit of searching, I discovered that there is fly autoscale This appears to be disabled by default. It would probably be a good idea to mention this somewhere next to hard_limit documentation.
Autoscaling is deprecated and doesn’t work with machines.
Machines are very low level, they’re the plumbing we’re using to replace Nomad for all apps. Most of our flyctl features are higher level (including scaling).
The best way to autoscale machines is to create as many as you want to have on at any given time, then leave them stopped. We’ll boot one when a request comes in.
This is probably how autoscaling is going to work for all apps in the future. fly autoscale max=50 will just create 50 stopped machines.