Sine and cosine is actually a decent solution for this.

local maxPitch = math.rad(15)
local speed = 0.1
function getAngle(t)
-- sine and cosine will oscillate between -1 and 1, so multiply it by maxPitch
return math.sin(t * speed) * maxPitch
end
-- Get a reference to the ship's position in it's starting position
-- Assuming that the ship has a forward-facing PrimaryPart
local ship = workspace.Ship
local baseCF = ship.PrimaryPart.CFrame
-- I'll use Stepped here because it returns the amount of time the game has been running.
game:GetService("RunService").Stepped:Connect(function(t)
local angle = getAngle(t)
ship:SetPrimaryPartCFrame(baseCF * CFrame.Angles(angle, 0, 0))
end)

Back again- this next question is more directed on transforming CFrame angles

Say the ship bobs this way with your script- I implemented it into something else but now I got this issue:

When the ship turns, the ship’s relative axis isn’t being bobbed, but rather the global one.
How do I transform it to do it more locally? If it helps, I’ve got the Y rotation saved as a single value in the script somewhere

EDIT: Got it; I simply referenced how the CFrame would be like with only the Y rotation, and then did * CFrame.Angles() with the extra rotations.