Multiple VAO share a VBO
VAO can share VBO's because they do not store vertex data itself but references to Vertex Buffer Objects. 

So you can first generate your buffers and upload your buffer data (vertex data and index data) to currently bound buffers.

glGenBuffers(1, &amp;VBO_xzCoord_IDs[i]);
glGenBuffers(1, &amp;VBO_yCoord_IDs[i]);
glGenBuffers(1, &amp;VBO_indices_IDs[i]);
glGenBuffers(1, &amp;VBO_normals_IDs[i]);
glGenBuffers(1, &amp;VBO_colors_IDs[i]);

//xz coords
glBindBuffer(GL_ARRAY_BUFFER, VBO_xzCoord_IDs[i]);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*xzCoords.size(), &amp;xzCoords[0], GL_STATIC_DRAW);

...

//indices
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBO_indices_IDs[i]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*indices.size(), &amp;indices[0], GL_STATIC_DRAW);


And then generate your VAO's and connect vertex array object data index you provide in glVertexAttribPointer call with currently bound GL_ARRAY_BUFFER.

The GL_ELEMENT_ARRAY_BUFFER can be a little confusing because it doesn't require glVertexAttribPointer call. So how it is connected with VAO? It seems that binding the buffer ties it to the currently bound VAO.

//VAO
glGenVertexArrays(1, &amp;VAO_IDs[i]);
glBindVertexArray(VAO_IDs[i]);

//xz coords
glBindBuffer(GL_ARRAY_BUFFER, VBO_xzCoord_IDs[i]);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);

...

//indices
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBO_indices_IDs[i]);

glBindVertexArray(0);


And in your rendering call you don't need to bind GL_ELEMENT_ARRAY_BUFFER because it was already connected with VAO.