So i'd like to suggest that not only x509 is problematic, but in certain configurations mutual TLS is also a non starter, though there are certainly other challenges in regards to certs themselves - for example, managing expiry dates properly, sometimes needing a CA with the proper chain of trust, other times needing to mess around with the formats for particular tech stacks (especially problematic in Java, in my experience, with its keystores and trust stores) etc.
That's one of the reasons why at work we recently moved one of our internal services over to JWT for server-server auth. Now, granted, i did ensure 100% coverage with tests for the code that i wrote and largely relied on already established libraries for the low level bits and therefore mostly just wrote glue code, but setting everything up was a breeze. Now, of course, you do need to figure out which approach to JWT is better for your app, from symmetric encryption with something like HMAC512 and a shared secret, to something like RSA512 that once again deals with private/public keys but doesn't force its own network topology upon you.
In combination with something like overlay networks with additional encryption (in the case of containers or a service mesh), you can get a "good enough" level of security, though depending on the circumstances you might also need to look into how loosely/strictly your tokens are also validated and write additional logic for the subject/claim/issuer/audience fields, issuance date and expiry date checks and so on.
When forced to deal with x509 certificates, however, i think the best option is to go for the simplest possible use cases (e.g. server cert for SSL/TLS, instead of mutual TLS) and to manage them at an ingress level, ideally with something like Let's Encrypt. Using them for more than that is just asking for additional challenges and/or trouble.