Simply set the output format to OpenEXR MultiLayer and save the render, it will store all render passes. Create another project for the compositing where you load the image(s) through an Image node. All render passes will be available as sockets, if you set the Layer in the Image node to (one of) the View Layer(s) you've rendered.
Example of multi-layer OpenEXR opened through an Image node and usage of the stored Cryptomatte:
There are three relevant options for the multi-layer OpenEXR file format in the context of compositing:
- Color (Channels)
- Color Depth
The color channels should always be set to RGBA in order to store associated alpha.
The color depth can either be set to Float (Half) (16 bit per channel) or Float (Full) (32 bit per channel). The latter is advisable in order to have the same floating point precision as the original render passes.
There are several options for codecs. They can be divided into two categories, lossy and lossless. The lossy codecs produce a smaller file size, but you're losing information through the compression. This should often be imperceptible, but may be undesirable for some compositing tasks, e.g. Cryptomatte. Unless you're constrained by limited storage space, I'd recommend using a lossless codec. In my tests it seemed that the PIZ codec is good trade-off between file size and encoding speed among the lossless codecs.
If you want to use the multi-layer OpenEXR files in other applications of your production pipeline, check if they support the codec you're intending to use before you start rendering.