All the major cloud providers now offer ARM-based instances alongside x86, priced lower for comparable specifications. For workloads that run well on them, the saving is straightforward and permanent. The question is whether yours does.
Interpreted and managed runtimes usually port without incident. Applications in Python, Node, Java, Go and .NET typically run on ARM with no source changes, because the runtime handles the difference. The friction, when it appears, is one level down.
Native dependencies are where migrations stall. A Python package with a compiled extension, a Node module with a binary component, a container image built for x86 only. Each needs an ARM build to exist, and most popular ones now do — but the one that does not will be the one your application cannot start without.
Container images need multi-architecture builds. Building for both platforms and publishing a manifest means the right image is pulled automatically, and it removes an entire category of confusing deployment failures where a pod refuses to start with an unhelpful message.
Benchmark with your own workload rather than trusting published comparisons. Performance per rupee varies by the shape of the work — memory-bound, compute-bound, heavily threaded — and the honest answer for your application might be better, worse or indistinguishable. Run it, measure cost per request, and decide from that.
The pragmatic path is incremental. Move a stateless service with few native dependencies first, watch it for a few weeks, then extend. A wholesale migration decided from a pricing page tends to surface every dependency problem at once.