What do you want to achieve?
I want to achieve a recoil system where the camera returns to it’s original position without going over it.

What is the issue?
While I have made a recoil system where the camera goes back to it’s original position, it only does so when you’re not trying to control it, but when you do control the recoil it obviously goes over the starting angle.
(Recoil values scaled to clearly show the issue)

Example without recoil control:

Example with recoil control:

What solutions have you tried so far?
I’ve tried different ways of saving the travelled recoil distance and moving it by that, but it seemed to not work and would make the camera be unable to go into negative angles

Here is my current recoil function (I use a sort of “velocity based” recoil system):

function updateRecoil(dt)
local recoil = math.clamp(verticalRecoilSpeed*recoilDirection*dt,-recoilTravelled,math.huge)
recoilDirection = math.clamp(recoilDirection-(2/recoilSpeed*dt),-1,1)
camera.CFrame *= CFrame.Angles(math.rad(recoil),0,0)
recoilTravelled += recoil
end

Well, now I feel stupid.
It wasn’t too hard of a solution, I just had approached it from the wrong angle.
All I had to do is while the recoil is going up, set the travelled recoil distance to the difference between the original angle and the current angle while clamping it, so it wouldn’t go below 0.

function updateRecoil(dt)
local camX,camY,camZ = camera.CFrame:ToEulerAnglesXYZ()
local recoil = math.clamp(verticalRecoilSpeed*recoilDirection*dt,-recoilTravelled,math.huge)
recoilDirection = math.clamp(recoilDirection-(2/recoilSpeed*dt),-1,1)
camera.CFrame *= CFrame.Angles(math.rad(recoil),0,0)
if recoil > 0 then
recoilTravelled = math.clamp(math.deg(camX-recoilStart.X),0,math.huge)
else
recoilTravelled = math.clamp(recoilTravelled+recoil,0,math.huge)
end
end

Whoops, made a slight mistake, as the provided answer only works for the first shot.
You also have to set the recoilStart to the current camera angle ONLY when recoilTravelled is 0.
Whoops~ =P
Fixed piece of code:

function updateRecoil(dt)
local camX,camY,camZ = camera.CFrame:ToEulerAnglesXYZ()
local recoil = math.clamp(verticalRecoilSpeed*recoilDirection*dt,-recoilTravelled,math.huge)
recoilDirection = math.clamp(recoilDirection-(2/recoilSpeed*dt),-1,1)
camera.CFrame *= CFrame.Angles(math.rad(recoil),0,0)
if recoil > 0 then
recoilTravelled = math.clamp(math.deg(camX-recoilStart.X),0,math.huge)
else
recoilTravelled = math.clamp(recoilTravelled+recoil,0,math.huge)
end
if recoilTravelled <= 0 then
recoilStart = Vector3.new(camX,camY,camZ)
end
end