There's different levels of static linking.
0) Application links to shared .so files in /usr/lib like libpcre.so. This is ordinary dynamic linking.
1) Application statically links all normal libraries like zlib and pcre, but still dynamically links to runtime systems like libc.so. I'll call this ordinary static linking. You can't "patch" a vulnerability in zlib without recompiling and redistributing, but on the other hand a zlib upgrade can't break your app either.
2) Application literally statically links everything (e.g. using musl instead of glibc). At this point the only dependency is the kernel you're running on. You need:
* obviously the kernel and architecture have to match. Can't run a 64-bit program on a 32-bit system without recompilation. Can't run a program compiled for linux on bsd.
* the kernel has to support whatever syscalls your application is using. If you're not using any fancy new functionality, you can get away with running on truly ancient kernels (e.g. 2.x linux)
* the kernel ABI must not break in the future. E.g. if linux changed the order of parameters to the open(2) call, that would be a breaking ABI change. Kernel devs are extremely careful to not do this, so I wouldn't worry about it.
The new packaging formats like Flatpak are based on containers. Your app thinks that it's using the "system" libpcre.so, but in actuality it's just being served the libpcre.so inside the container's filesystem (linked by the ld inside the container's filesystem, using the glibc inside the container's filesystem, and so on). So basically the same high level pro/cons as #2 above, but easier for users and developers.