AWS Cost Explorer kept nagging me. Every month it would cheerfully point out that I could save a respectable amount of money by migrating my EC2 instances from x86 to AWS Graviton. The recommendation wasn’t subtle. It was persistent. According to AWS, my workloads were excellent candidates for ARM.
On paper, it made perfect sense. ColdFusion runs on Java. Java runs beautifully on ARM. So… why wouldn’t ColdFusion?
How hard could it be?
That question has launched countless engineering rabbit holes over the years. This one was no exception. I figured I’d spend an afternoon proving that ColdFusion worked, swap a few instances over to Graviton, and pocket the monthly savings.
Instead, I spent days chasing obscure incompatibilities through installers, web server connectors, native binaries, AJP packets, Apache modules, Docker containers, and more than a few moments of questioning my life choices.
The funny part is that ColdFusion itself wasn’t the problem. In fact, once you get past the installation hurdles, ColdFusion is surprisingly happy running on ARM. Java certainly doesn’t care what CPU architecture it’s executing on.
The real problem turned out to be everything around ColdFusion.
This article documents the journey, the dead ends, the discoveries, and ultimately why something that seems so obvious still isn’t a supported deployment architecture. Hopefully it saves the next curious developer from repeating the same experiment… unless, of course, you’re into that sort of thing.
First, a Little Architecture Problem
There was one important catch before I could even begin. You can’t just change an existing EC2 instance from x86 to Graviton. Graviton processors use the ARM64 architecture, so this wasn’t going to be a matter of stopping an instance, changing the instance type, and starting it again. I needed a new ARM64 instance.
For the experiment, I spun up an AWS Graviton instance using Ubuntu 24.04 ARM64. The eventual test environment looked like this:
- AWS Graviton ARM64
- Ubuntu 24.04
- Apache 2.4.58
- Adobe ColdFusion 2025 Update 11
- Java 21 ARM64
My existing infrastructure uses Apache in front of ColdFusion, with Apache talking to ColdFusion through the Adobe web server connector and AJP. That detail becomes rather important later.
But first I had to get ColdFusion installed.
Problem Number One: The JVM
The ColdFusion installer isn’t designed for ARM64. That wasn’t particularly surprising. Adobe doesn’t advertise Linux ARM64 as a supported ColdFusion platform, so I knew from the beginning that I was wandering off the edge of the map.
ColdFusion also ships with its own Java runtime. An x86_64 Java runtime. An x86_64 executable isn’t particularly useful on an ARM64 machine.
Fortunately, this was one of the easier problems to solve. I installed an ARM64 build of Java 21 and replaced the bundled ColdFusion JVM, preserving the original at:
/opt/coldfusion2025/jre-x86_64-original/
And then something interesting happened. ColdFusion started. Not “sort of started.” Not “started after emulating x86.” ColdFusion was actually running natively on ARM64 Java.
That was the first big result of the experiment. ColdFusion itself can run on ARM64.
This makes sense when you think about what ColdFusion fundamentally is. The CFML engine is running inside the JVM, and the JVM abstracts away most of the underlying processor architecture.
At this point I was feeling pretty smug. AWS was right. Java was right. I was right. Time to hook up Apache and declare victory.
Narrator, probably in that smooth Morgan Freeman timbre: he did not declare victory.
Enter wsconfig
If you’ve administered ColdFusion for any amount of time, you’re probably familiar with Adobe’s Web Server Configuration Tool, wsconfig. On the other hand, if you administer ColdFusion and aren’t familiar with wsconfig, you should be.
wsconfig is Adobe’s Web Server Configuration Tool. It’s what connects an external web server such as Apache or IIS to ColdFusion, installing and configuring the native connector that passes requests between the two.
A few best practices are worth remembering:
- Use
wsconfigrather than manually copying connector files around. Let ColdFusion create and maintain its connector configuration whenever possible. - Re-run the connector after major ColdFusion updates when Adobe requires it. Updating ColdFusion doesn’t necessarily mean your existing web server connector is current.
- Know which ColdFusion instance a connector points to. This becomes particularly important on servers running multiple instances.
- Back up your web server configuration before changing connectors.
wsconfigmodifies Apache or IIS configuration, and rollback is much easier when you know exactly what changed. - Don’t treat the connector as an invisible piece of plumbing. Know where its configuration and logs live. When requests reach Apache or IIS but mysteriously don’t reach ColdFusion, the connector is one of the first places you should look.
For most ColdFusion installations, you run wsconfig, it does its job, and you rarely think about it again. Unfortunately, I was about to become very familiar with it.
I ran it. It generated the expected Apache configuration. And Apache promptly refused to load the connector.
The reason became obvious once I inspected mod_jk.so. Adobe ships a compiled web server connector with ColdFusion. That binary was compiled for x86_64. Apache was ARM64. You cannot load an x86_64 Apache module into an ARM64 Apache process any more than you can put a Volkswagen transmission into a toaster.
This was the moment the experiment changed. ColdFusion wasn’t preventing me from running on ARM. Adobe’s ColdFusion web server connector was.
Fine. I’ll Build My Own.
The Adobe connector is based on Apache Tomcat’s mod_jk. And mod_jk is open source. Problem solved, right?
I installed the necessary development tools, including apache2-dev and build-essential, downloaded the Apache Tomcat Connectors source, and compiled mod_jk 1.2.50 directly on the ARM machine.
That produced exactly what I wanted: a native ARM64 mod_jk.so.
Apache could load it. For a few glorious minutes, it looked like this entire ridiculous exercise was about to work. Then I tried to actually use it.
Adobe's mod_jk Isn’t Quite mod_jk
The first problem was configuration. The configuration generated by Adobe’s wsconfig contained directives and worker properties that stock Apache mod_jk didn’t recognize.
Among them were JkWorkersFileReload and properties including:
heartbeat_intervalheartbeat_limitworker.cfusion.heartbeat_servlet_pathworker.cfusion.monitoringsecret
These aren’t part of the standard connector I had just compiled. So I started stripping away the Adobe-specific pieces. Eventually I reduced the configuration to the essentials needed to establish an AJP worker, including the ColdFusion AJP port and secret.
Apache started. mod_jk loaded. The worker connected to ColdFusion. Now we were getting somewhere. It was time to declare victory.
Narrator, probably in that smooth Morgan Freeman timbre: he did not declare victory.
Requests still didn’t work.
Unknown AJP protocol code: 0F
The Apache logs started reporting something I hadn’t seen before: Unknown AJP protocol code: 0F. That was considerably more interesting than a configuration error.
Apache was connecting to ColdFusion. ColdFusion was responding. But the response contained an AJP message that the standard Tomcat connector didn’t understand.
At first there were plenty of reasonable things to suspect. Maybe I had an AJP configuration mismatch. Maybe this was related to request attributes. Maybe the secret configuration was wrong. Maybe Adobe’s connector expected some subtly different handshake.
So I kept digging.
Eventually, there was only one reasonable thing left to do. Look at the actual bytes travelling across the connection.
Down to the Wire
A packet capture removed any remaining ambiguity. ColdFusion sent this:
41 42 00 0e 0f 00 0a /index.cfm 00
The interesting byte is:
0f
Standard AJP defines a collection of packet types used for communication between a web server and an application server. 0x0F isn’t one of the messages understood by stock mod_jk 1.2.50, and ColdFusion was absolutely sending it.
Even more revealing was the payload:
/index.cfm
This wasn’t random garbage, a corrupt packet, or an architecture problem. It was deliberate protocol behaviour. Adobe’s ColdFusion connector uses an extension to the standard AJP protocol. Their version of mod_jk knows what 0x0F means. Apache’s upstream version doesn’t.
And that was the wall.
Well, That’s Annoying
At this point the architecture looked almost comical.
- ColdFusion 2025 was running on ARM64.
- Java 21 was running natively on ARM64.
- Apache was running natively on ARM64.
- I had compiled
mod_jknatively on ARM64. - Apache could connect to ColdFusion over AJP.
- ColdFusion could respond over AJP.
...and the entire thing fell apart because Adobe’s implementation speaks a slightly different dialect of AJP that only Adobe’s connector understands. The connector Adobe provides is compiled for x86_64.
So I went looking for source code.
If Adobe’s connector was based on mod_jk, perhaps the changes were available somewhere in the ColdFusion installation and I could simply apply Adobe’s modifications to the ARM64 build.
No such luck.
The installation contained the connector binaries and wsconfig tooling, but I couldn’t find the source for Adobe’s modifications. Without knowing exactly how Adobe implemented its extensions, I wasn’t interested in reverse engineering an application server protocol and maintaining my own ColdFusion connector.
There’s experimentation, and then there’s volunteering to become the maintainer of an unofficial ARM64 Adobe ColdFusion web server connector.
I have hobbies.
So Does ColdFusion Run on ARM?
Yes. And no. That’s what makes the result interesting.
If the question is, "Can the ColdFusion 2025 runtime execute on ARM64 Linux?" My experiment says yes. I ran ColdFusion 2025 Update 11 on Ubuntu 24.04 ARM64 using an ARM64 Java 21 JVM. The ColdFusion runtime itself was not the thing that stopped the experiment.
If the question is, "Can I take a conventional supported-ish ColdFusion deployment using Apache and Adobe’s web server connector and move it to AWS Graviton?" Not with the components Adobe currently provides. The Adobe Apache connector is an x86_64 native binary, and replacing it with stock ARM64 mod_jk doesn’t work because ColdFusion uses Adobe-specific extensions to the AJP protocol.
That distinction matters.
Saying “ColdFusion doesn’t run on ARM” isn’t technically accurate. It does. The ecosystem required to deploy it the way most of us actually deploy ColdFusion is the problem.
Could You Make It Work Anyway?
Almost certainly. Given enough determination, there are several directions someone could investigate.
- You could reverse engineer Adobe’s AJP extensions and patch upstream
mod_jk. - You could investigate alternative proxy architectures that don’t depend on Adobe’s Apache connector.
- You could run portions of the stack under x86 emulation.
- You could put ColdFusion into a container and start experimenting with mixed architectures.
- You could probably construct something sufficiently elaborate that eventually an ARM64 processor serves a
.cfmfile to the Internet.
But that wasn’t the question I was trying to answer. I wasn’t trying to win a science fair. I was trying to save money on AWS.
Any infrastructure change needs to be evaluated against operational cost as well as compute cost. Saving a few dollars on EC2 while introducing an unsupported, custom-built web server connector that I now have to maintain is not a cost optimization. It’s a hostage situation.
For production infrastructure, I want boring. Boring is good.
The Part I Find Most Interesting
The experiment exposed something about ColdFusion that normally remains completely invisible. Most ColdFusion developers don’t spend much time thinking about the web server connector. You install ColdFusion. You run wsconfig. Apache or IIS starts sending requests to ColdFusion. You get on with your life.
But there’s native code hiding in that architecture. That’s significant as the rest of the infrastructure world increasingly embraces ARM.
Java has excellent ARM64 support. Linux has excellent ARM64 support. Apache has excellent ARM64 support. PostgreSQL, MySQL, Redis, Docker, nginx and enormous portions of the modern server ecosystem already run happily on ARM.
AWS has been pushing Graviton aggressively because ARM can provide compelling price/performance improvements.
ColdFusion is tantalizingly close. The Java application itself appears capable of making the jump. The native integration components around it haven’t.
What Would Adobe Need to Do?
From what I found, Adobe wouldn’t necessarily need to undertake some enormous ARM port of the ColdFusion runtime. Much of the hard work has effectively already been done by the Java ecosystem. The immediate missing piece is much smaller and much more mundane:
Provide ARM64 builds of the native web server connectors.
Or, alternatively, document and upstream the ColdFusion-specific protocol extensions so the standard connectors can interoperate with ColdFusion.
There may be other native ColdFusion components or features that would reveal architecture dependencies under broader testing. My experiment wasn’t an exhaustive certification suite, and successfully starting ColdFusion doesn’t prove every ColdFusion feature works correctly on ARM64.
That’s precisely why I wouldn’t call this production-ready, but what I found is encouraging. The fundamental obstacle wasn’t CFML. It wasn’t Java. It wasn’t Linux. It wasn’t Graviton.
It was a web server connector.
Was AWS Cost Explorer Right?
Technically? Yeah. Which is irritating.
Moving appropriate workloads to Graviton really can reduce AWS costs, and the software stack underneath ColdFusion is increasingly architecture-independent. AWS looked at my infrastructure and effectively said, “Hey, you could spend less money if you moved this to ARM.”
And I looked at ColdFusion running on Java and thought, “That’s actually a pretty good idea." Several days later I was staring at hexadecimal AJP packets trying to figure out what the hell 0x0F meant.
Plot Twist: The Graviton Migration Happened Anyway
After all of that, there’s an important correction to make. The Graviton migration didn’t fail. Adobe ColdFusion on Graviton failed. Those aren’t the same thing.
The whole experiment had already demonstrated that CFML wasn’t inherently tied to x86. Java ran on ARM. Apache ran on ARM. ColdFusion itself ran on ARM. What ultimately stopped the Adobe deployment was a native, architecture-specific piece of infrastructure sitting between Apache and ColdFusion.
So rather than abandon Graviton, I changed the question. If Adobe ColdFusion couldn’t give me a practical production stack on ARM64, could another CFML engine?
CommandBox Changes the Equation
At this point I had proven something useful: CFML itself wasn’t the problem. So rather than give up on Graviton, I decided to try the application under Ortus Solutions CommandBox.
And after everything I’d just been through, the experience was almost comical.
CommandBox installed on the ARM64 server without drama. I configured the CFML server, pointed it at the application, started it up and... It worked.
Pretty much everything worked out of the box. Forgive the pun.
There was no native Apache module to compile. No wsconfig. No proprietary AJP packet showing up in Wireshark. No mystery binary built for the wrong processor architecture. The CFML application was simply running on ARM64.
Apache Got Simpler Too
I still wanted Apache sitting in front of the application. It was already part of my infrastructure, and I wanted it handling the public HTTP and HTTPS traffic rather than exposing the CommandBox server directly.
But instead of loading a ColdFusion-specific module into Apache and communicating with the CFML engine over AJP, Apache could simply act as a reverse proxy.
Conceptually, the architecture became:
Internet → Apache → HTTP → CommandBox → CFML application
Apache accepts the public request and proxies it to the local port where the CommandBox server is listening.
That’s it.
No architecture-specific ColdFusion connector is sitting between the web server and application server. From Apache’s perspective, CommandBox is simply another HTTP application running upstream. That distinction is what made the whole ARM problem disappear.
Apache already runs natively on ARM64. Java already runs natively on ARM64. CommandBox gave me a CFML server architecture that didn’t depend on Adobe’s x86_64 web server connector. Once Apache was configured to reverse proxy requests to CommandBox, the application behaved exactly as I expected it to.
Fuck. I should have done this in the first place.
The Migration I Thought Would Be Difficult Wasn’t
I expected moving the application away from Adobe ColdFusion to uncover another list of problems. It didn’t.
This is a real application, not a Hello World test designed specifically to prove that CFML can execute on ARM. There was plenty of opportunity for engine differences, Java dependencies or assumptions in the application to make themselves known.
Instead, the application came up and worked. That was probably the most surprising part of this entire exercise.
I’d spent an unreasonable amount of time trying to move one native connector across processor architectures. Once I stopped requiring that connector, the ARM64 migration became remarkably ordinary. And ordinary infrastructure is exactly what I wanted.
The resulting architecture is arguably simpler too. Apache speaks HTTP to the application server using the same reverse-proxy pattern used by countless other application stacks. The CFML engine isn’t coupled to Apache through a native module compiled for a particular processor architecture.
If I change the underlying CPU architecture again someday, Apache doesn’t care. The application doesn’t care. Java doesn’t care. And, most importantly, I don’t have to care. I love it when I don't have to care.
So Did the Graviton Migration Work?
Absolutely. The Adobe ColdFusion experiment didn’t produce the deployment I originally expected, but the AWS recommendation that started this entire adventure turned out to be perfectly achievable.
The application is running on Graviton. It’s running CFML. It’s running behind Apache. And it’s doing so without the architecture-specific connector that stopped the Adobe ColdFusion version of the experiment. There is a certain irony in spending days figuring out precisely why something won’t work, only to discover that changing one piece of the architecture makes the entire problem disappear.
But that’s also the useful lesson here. The question wasn’t really whether Adobe ColdFusion could run on ARM64. We proved that, at least at the runtime level, it could. The better question was whether my CFML application could run on ARM64 in a production architecture I was comfortable maintaining.
CommandBox changed the answer from “almost” to “yes.” A huge shout-out to Ortus Solutions. CommandBox took what had become an absurd ARM migration headache and made it almost boring. Install it, configure it, start it, and it just worked.
So what did I learn?
AWS was right. Java was right. ARM was ready. Even ColdFusion itself was willing to come along for the ride. I just spent several days arguing with one little piece of native code before realizing I didn’t need it at all. The Graviton migration succeeded. The application is running happily on ARM64, Apache is happily reverse proxying to CommandBox, and the whole stack is simpler because of it.
Would I do it again? Absolutely.
Would I start by compiling mod_jk and analyzing AJP packets? Fuck no.
Sometimes the best solution isn’t figuring out how to make the old architecture work. It’s realizing you don’t need the old architecture anymore.