I can't share the name, but we've built a complete enterprise stack on Mono. There are several parts:
- Server, Nancy2/Marten/Hangfire, runs on linux
- Headless clients for linux and Windows (x64)
- GUI clients for macOS, linux and Windows (x64)
The server is deployed as a static fat binary. The headless clients are static binaries for linux and .NET proper for Windows.
The GUI clients use Eto, which in turn means:
- Static binary using Eto.Gtk on linux
- Static, signed app bundle (Gatekeeper friendly) using Eto.Xamarin on macOS
- .NET proper application using Eto.Wpf on Windows
No performance issues so far. I wish Mono would implement a GUI for the profiler though. The log profiler is great to use, but clunky in comparison with dotTrace or NProfiler.
The whole architecture sounds complicated but really isn't. It's a single solution with three* projects:
- Headless Clients
- Single Eto GUI for all platforms
- (* Target build projects for the GUI platforms, no code in here)
The build server then only needs to do some additional work for creating DMG installers and signing the macOS app.
How's Eto? It seems really cool but it's backed by a small company, I think there's just 1 developer.
Also, do your GUI clients have fancier UIs? For example, have you implemented your own grid view, something where you put custom controls in the cells, images, graphics, etc.?
We maintain internal forks of Eto and Nancy. They are easy enough to grok for us to modify and maintain, even when development upstream dies.
Most of the user interaction is happening via the web app served by the server. The local clients are "just" for configuration, comparable in complexity with e.g. Tunnelbear or Glasswire. We use some custom grid elements, which have been easy to implement.
Eto is the only framework out there which gives you native GUIs everywhere. That's what was most important to us. Native speed, native OS integration (tray menus, notifications etc.). It's also really easy to learn for devs who might have been WPF-exclusive before.
A sort of follow-up question: have you contributed things back to Eto? Were they responsive?
Thank you for your detailed answer, by the way! :)
No, most of the small customizations are very specific to our application. Mostly how closing events or notifications are handled. TBH at least I don't think much about Eto day to day, it's just there and works. Questions mostly have been answered by existing issues, where the author seems responsive enough.
Try Xamarin Profiler as a GUI :). It supports loading mlpd files and makes your life 10x easier (if it doesn‘t crash).
Never having used C# myself but I sometimes play https://github.com/OpenRA/OpenRA (an open source Command & Conquer clone) which is written in C#, runs on all major OSes and uses Mono at least on Linux.
Wow that is crazy. C&C was a childhood favourite, and I'm so happy to hear it has been cloned as free software!
Syncplicity, a desktop file synchronization application, uses Mono on MAc.
The UI is native: WPF on Windows, Objective C on Mac. The core synchronization logic is shared C# code. (Objective C and C# co-exist via Pinvokes and function pointers.)
We started with Mono in 2010 before a lot of the modern tools, libraries, ect. At the time mono was very buggy. Now, Mono is a lot more stable.
As far as insights go:
We originally had, on Mac, a C# UI built with a (now obsolete) framework called MonObjC. The problem with MonObjC was that the only way to understand it was to be an Objective C expert; and it didn't always wrap the APIs in ways we needed. Thus, when we had some lawyer come and tell us that they didn't like the license, we just rewrote everything to PInvoke into shim C functions that call into Objective C. Thus, IMO, when in doubt, PInvoke into native code instead of fighting a compatibility layer.
Our Mac app packaging is all homegrown, primarily because our application predates the more modern mono packaging. I haven't tried the newer app packaging that comes with Visual Studio on Mac. IMO, make sure you understand app packaging.
Quite frankly, we've always had trouble with live debugging. (This might be due to our older packaging scheme.)
Ultimately, you need to understand the cost of writing something twice. When you work with Mono, or any cross platform framework / language / ect, you won't "write once." Instead, you will write once, debug on all platforms, and then add a bunch of special cases. Then, the developers who work on platform X will make a change that breaks platform Y because it wasn't covered in a unit test. Sometimes the cost of writing something twice really is worth it compared to the tradeoffs. (This certainly is the case with our UI, where we want a 100% native UI.)
Edit: We use dependency injection. (Via code, not a framework.) We have a lot of base classes in common code, and then platform-specific subclasses to handle special cases.
Bitwarden  uses C#, and is portable.
You mean like all those apps running on iOS and Android on top of Xamarin?
Smilebooth's backend runs on mono on linux, but for private / no internet events we run a local version of the server on .net 4.x and windows. Same binary. F#/suave.io server. I was shocked ~5 years ago when I tried the .net binary on mono/linux and it just worked (iirc the only change was the command line arguments passed to imagemagick). But it did and it has, so no need to change.
I left that project two years ago but if starting something similar today I'd probably go with .net core. Not because of any issues with mono, but core just seems like the future. I didn't even realize that mono was still in active development, though I'm not really following that field anymore, so.
Just for the record, I never did notice any memory leaks that others complain about. Our servers stayed running for months at a time with no noticeable leaks.
Do x-platform games using mono within Unity count?
I am the main developer on an open source app called Radarr (https://github.com/Radarr/Radarr). It focuses on automatically downloading movies and managing said movies.
While we have used mono only one „one“ platform technically (Unix), I would still like to add my own experience, since we encountered some different behaviour on macOS and Linux. Also I have spent the last week or so tracking down memory leaks for our App :)
First of all, tracking down memory leaks with mono is really not fun. The chances are very high, that the memory leak does not occur in the managed parts of either your or other people‘s code, else the garbage collector should have collected that anyways. While you do have an integrated memory profiler, it seems that they like to change them every so often and even the current one isn‘t very compatible. E.g. one user sent me a profiling report, that I could neither open via GUI nor via the commandline. Furthermore, the only GUI option available is Xamarin profiler, which does not run on Linux (more on that later). While it does the job, it likes to crash a lot and eats up a huge chunk of memory, especially if you have a large profiling snapshot.
Since I had to deal with unmanaged leaks (as indicated by the managed snapshots I collected), I tried to use valgrind. However, it seems that mono uses SIGSEGV instead of checking for null pointers beforhand and then uses signalhandlers to throw NullPointerExceptions. This would normally not be a problem, but one part of the mono System runtime is very likely to have a NullPointerException. Thus valgrind sees a SIGSEGV and mono‘s signal handler playing around with the stack. It decides that this is not acceptable and instantly termiantes the app. After I patched that out, valgrind seemed to work, except that it didn‘t find any leaks and the memory leak was not even there anymore!
So I just started randomly commenting out lines of functions that could potentially be involved and got lucky. I still haven‘t figured out why that line of code leaks memory though.
Furthemore, I also found some memory leaks inside mono‘s native HttpWebResponses.
We are still trying out figure out another memory leak that occurs and haven‘t had any luck finding out why. It‘s especially hard, since this particular memory leak only occurs on Linux. (If you want to see the full discussion I recommend you look at issues: https://github.com/Radarr/Radarr/issues/1580 and https://github.com/Radarr/Radarr/issues/3157).
On the other hand, from my experience the performance itself seems almost as good as on Windows, save for the memory leaks. Some big requests to the backend of the app might take 5-10% longer on mono, but that‘s negligble IMO.
So, in short, if you don‘t have any issues with performance it‘s good. However, as soon as you do have some issues, you will be faced with a lot of problems. There is no documentation on profiling or what could cause problems, except for a few standard things that‘s more generally .NET related. By pure luck, I found a guid that shows how you can do profiling for your mono app in Instruments, but that didn‘t help either, since the leaks only occur on Linux.
Nevertheless, we are planning on moving to .NET Core anyways, so no I would not use mono.
Sonarr and various similar utilities such as Emby also use .NET. The memory leaks of Emby are killing me on a Raspberry Pi but otherwise the performance is OK. And you can safely restart the service (be it Docker or native systemd/sysv or whatever) at 4 AM.
What are you going to switch to, btw?
OpenSimulator  runs on .NET and Mono (for Linux and Mac).
That reminds me, the annual OpenSimulator Community Conference is this weekend .
It's a very mature project now, has been around more than a decade. I guess if it were started today, it would use .NET Core.
Emby - an alternative to Plex.
I have run it on Linux, and did not find any issues. Everything still worked.
Well, actually, it has/had a memory leak. Not sure if that's just on the Linux platform.
Duplicati uses C# and I use it on Linux, but I know few people using it on windows too.
On Linux the UI is via the browser (the service has a web interface).
I think this is the only C# project I use currently.
Not a developer of it, but banshee was one of my favorite media players and ran on mono. I moved away from it, because it took too much CPU compared to deadbeef.
KeePass was using mono.
Keepass2 runs on Mono.
I have a more general question: has anyone here built a truly portable app using Mono (i.e. one that runs on several platforms, has a reasonable number of active users and is supported) and can share some insights regarding performance and other issues? If you started today, would you still use Mono?
I think some people tend to think like a compiler when they write code, and when you do, you less often get interesting compiler driven improvements. You are already folding constant expressions, lifting loop invariants out of loops etc.
I've tried using Souper on a number of occasions to micro-optimize handwritten LLVM code and every single time it just returned the same code to me with different formatting. Has anyone else had better success with it? Perhaps all my cases where already optimal, but I have a hard time believing that. Or perhaps I should try again since it's been a few years.
Does anyone know how well does mono to LLVM perform in contrast to standard RyuJIT on .Net Fx or .Net Core?
Nop, they use old dotnet framework, on windows, even for their embedded stuff. They use C# and F#.
Genetec, on of the largest Video Management systems does everything in Mono.