If your deployment environment employs the
CREATE opcode under the hood, The deployed contract address is going to be a function of the deployer's EOA and the nonce.
contractAddress = hash(deployerAddress, deployerNonce)
You could get the same contract address if you deploy the contract using the same wallet and the same transaction order (i.e., the contract creation TX is the 1st, 2nd, or 3rd, ... tx in that wallet on both chains).
Otherwise if the deployment environment is using
CREATE2, the contract address is going to be a function of the init code of the contract (creation bytecode) and a salt value of your choosing. By allowing you to set any value for salt, CREATE2 allows you to pick the future address of a contract before it's deployed. To have the same deployed contract address here on every chain, you need to use the same machine to compile and deploy the same contracts. The compilation directory affects the init code on some environments like Truffle, that's why it's recommended to use the same machine. Also, any change in the contracts code would result in a different init code, so you should have a code freeze before deploying to any chain. You will also need to use the same value for the salt.