Pay attention that the correct time zone (UTC in your case) is applied during the conversion. If you are not explicit about this, the time zone of the current session is assumed - typically not UTC.
ALTER TABLE tbl ALTER ts_column TYPE timestamptz USING ts_column AT TIME ZONE 'UTC';
Works for any time zone. Unfortunately, it rewrites the whole table blocking every other access to the table for the time. For big tables, or under concurrent access, this can be an isssue. Since Postgres 12, there is a juicy exception for UTC timestamps. The release notes:
ALTER TABLE ... SET DATA TYPE changing between
timestamptz to avoid a table rewrite when the session time zone is
UTC (Noah Misch)
In the UTC time zone, these two data types are binary compatible.
So, do this instead:
SET timezone = 'UTC'; -- make sure the time zone is set properly
ALTER TABLE tbl ALTER ts_column TYPE timestamptz;
-- RESET timezone; -- possibly
In addition, check a possible column default. Any expression working with data type
now()::timestamp) is subject to the same problem. To change:
ALTER TABLE tbl ALTER ts_column SET DEFAULT now(); -- or current_timestamp
Obviously, statements writing to the table also need to use
timestamptz now - or you have another instance of the same problem with automatic conversion from the type
timestamp [without time zone].
Since this is a production DB, best do it all in a single transaction to avoid race conditions - or even a single statement:
SET timezone = 'UTC';
ALTER TABLE tbl
ALTER ts_column TYPE timestamptz
, ALTER ts_column SET DEFAULT now();