What's the actual difference between a GitLab epic and an issue for feature requests?
A GitLab issue can only be created inside a project. A GitLab epic can only be created inside a group, and it's a Premium or Ultimate feature, not something available on Free. So for a customer request that lives inside one project, it usually isn't much of a choice at all. It's an issue, and if it needs a bigger container than one issue, that container is a milestone, not an epic.
Epics exist to group work across projects. "An epic can be created at group level while an issue can only be created at project level," one person wrote when opening a thread on the GitLab forum asking exactly this question. That line was the asker's own starting assumption, not a GitLab team member's answer to it, but it's an accurate one. An epic is the parent of one or more issues, sometimes spanning several projects under the same group. If a request only touches one repo, an epic doesn't add anything an issue plus a milestone doesn't already give you, and it costs a tier you might not have. If a request starts as an issue and later turns out to need an epic, GitLab does let you promote it, one direction only; there's no documented way back.
Group-only isn't a technicality
It's tempting to read "epics are group-level" as a UI quirk you can work around. It isn't. GitLab has tracked a request for project-level epics since 2019, issue 31840, "Provide Project Level Epics." GitLab closed the issue in July 2024, but not because the work shipped in full. Project-level epics landed behind a feature flag on Premium, while navigation, roadmap, and board integration never followed, and nested epics stayed Ultimate-only. If a GitLab instance has one project per team, or one project per customer-facing product, the group/project split matters immediately. There's no smaller epic to reach for.
That's also why epics show up less often than people expect on smaller GitLab setups. A team on Free doesn't have epics at all. A team on Premium whose single project sits inside a group still has epics available, just nothing multi-project to use them for. The feature is real and useful, but it answers a different question than "how do I track this feature request." It answers "how do I track this initiative across the five projects it touches."
What a single-project request should actually use
For the common case, a request that lives entirely inside one project, the two building blocks are the issue itself and a milestone. GitLab's milestone docs confirm milestones exist at both the project and group level and are available on Free, unlike epics. A project milestone groups issues and merge requests toward a goal, which is exactly the shape of a feature request that needs three or four issues to ship: a backend issue, a frontend issue, a docs issue, all under one milestone with a due date.
This gets you almost everything an epic gives you for a single project: a shared parent, a progress view, a target date, without needing a group-level container or a paid tier you don't have. The gap is that a milestone doesn't carry the epic's roadmap and hierarchy features, start and due date rollups across child epics, for instance, but for one project's worth of work that gap rarely matters.
Converting one into the other
The primary question that sends people looking for this comparison is usually not which to pick. It's whether they picked wrong, and whether they can fix it. Partially, yes. GitLab documents a one-way path: you can promote an issue to an epic directly from the issue's "Change type" menu, and the title, description, comment threads, upvotes, and participants all carry over. The catch is the requirement baked into the name. Promotion only works if the project belongs to a group, and it takes at least the Planner role on that parent group to do it.
There's no documented path the other direction. GitLab doesn't offer a way to demote an epic to an issue. If a request turns out to be smaller than expected, the practical fix is closing the epic and moving its content into a single issue by hand, not a built-in conversion. That asymmetry is worth knowing before defaulting to epics "just in case." Going up from issue to epic is one click. Going back down is not.
The SSO request that Mara almost filed as an epic
Anchorpoint Data ships a data pipeline product out of a single GitLab project. Mara Jensen is the product manager there, and she's usually the first person a customer's IT team reaches when something needs to change. A customer's IT lead emailed asking for SAML-based SSO before their security review would approve renewal.
IT lead (customer): We need SAML SSO configured before our next audit cycle. Is this on your roadmap?
Mara: It is now. I'm tracking it as issue #412 and I'll loop you in once we scope the auth provider work.
Mara's first instinct was to open an epic, since the request touched three separate pieces of work spanning the auth service, the admin UI, and the docs. She went looking for the epic creation button inside the project and couldn't find one. Anchorpoint is on GitLab Premium, but its project lives under Mara's personal namespace, not inside a group at all, so there was no group context to attach an epic to in the first place.
Instead, she filed one issue for the customer request, then broke the implementation into three linked issues (auth service, admin UI, docs) under a milestone titled "SSO, Q4." The milestone view gave her the rollup she actually wanted, three issues, one due date, one place to see how much was left. When a second customer asked for the same thing two weeks later on a support call, Mara added a comment to issue #412 rather than opening anything new. She also knew that comment was the only place the second customer's name existed. Nothing in GitLab would have surfaced that a request already existed if the second person had emailed a different teammate instead of mentioning it on a call.
Where issues and epics stop answering the actual question
Neither container was built to answer how many customers asked for something, and who they are. An issue holds one thread of conversation. An epic holds multiple issues. Both assume a request has already been noticed and already been slotted into the right container. Neither one helps notice that the same ask showed up in a support ticket, a Slack thread, and a sales call before anyone opened a GitLab item for it at all.
That's the point where teams stop trying to solve this inside GitLab's containers. Modem watches connected GitLab projects for issue and merge request activity alongside Slack, support tools, and sales calls, and turns each request into a topic that tracks who asked, regardless of which container the eventual work lands in. When Mara's second customer mentions SSO on a call, Modem links that mention to the same topic as the first customer's email and the GitLab issue tracking the work, instead of it becoming an untracked comment on issue #412 that only exists if someone remembers to write it. We build Modem, so weigh that against the alternatives compared in the best tools to turn customer feedback into GitLab issues. Below the point where requests arrive faster than one person can track who's asked, an issue plus a milestone is genuinely enough. See what a customer context graph is for the shape of what changes once it isn't.
The smallest version you can start this week
If a request lives in one project, open an issue, and if it needs more than one issue to ship, add a milestone rather than reaching for an epic. Reserve epics for work already known to span multiple projects under the same group, on a tier that has them. And if an issue gets promoted to an epic that turns out to be smaller than expected, plan on closing it and folding the work back into one issue by hand. There's no undo button for that particular decision.
