The problem that you encounter is called "z-fighting" and it occurs when two objects with the same depth (z value) are rendered.
In this example, we take two objects called ObjA and ObjB. They are the same, but have different colors. Due to the very small error that you get with fixed precision floating point numbers, ObjA may have a lower fragment depth than ObjB for a given pixel and thus ObjA's color is saved to that pixel, but ObjB could have a lower fragment depth for another given pixel and ObjB's color is then saved to that pixel. Which object will have a lower depth value is quite random and causes that flickering.
There are two ways to fix this. Which one works best for you, depends on the program.
Disabling depth testing on the second surface that is being drawn. The first surface obviously draws well, and the problem comes when drawing that second surface. By disabling the depth test, OpenGL just draws every fragment to the framebuffer and you do not have that problem with "which fragment has the lowest depth".
If you only disable the depth test for the second surface and enable it again for the other surfaces (and render the second surface after the first surface), then it should work as the first surface still writes the depth values.
This approach starts to break, when the second surface either does not fully align with the first surface or the second surface is shaped in a way so that it overlaps itself. (When rendering a sphere, for each pixel you always render two fragments, the front side and the back side of the sphere. The back side would then not be culled away.)
Adding a bias to the depth values of the first surface. The problem is that both surfaces have the same depth value for a given pixel, but if you offset the depth values for one of them, then you do not have that problem.
gl_FragColor which allows you to set the color in the framebuffer,
gl_FragDepth allows you to set the depth in the depth buffer. You could get the normal depth value from
gl_FragCoord (as the documentation says) and add a small amount to it. Doing this, does not actually change anything of the mesh. The mesh does not move around and it does not go further into z-space. The shader simply writes a different depth value to the depth buffer. This depth buffer is only used for depth testing and can thus easily be changed to get the effect we want. This only needs to be done for the first surface and not the second surface or any other surface.
When the bias is too large, artifacts can visibly show up around intersections with the surface, but when the bias is small it should not be noticeable.