# Cockpit CMS vale a pena? 12 lições de quatro sites em produção

> O Cockpit CMS é um bom CMS headless? Doze lições de quatro sites em produção com o Cockpit v2, o CMS open-source e self-hosted em PHP: cache do schema, i18n, API keys, backups e erros.

- **Source:** https://amplifiedcreations.com/pt/journal/cockpit-cms-production-lessons
- **Publisher:** amplified creations (https://amplifiedcreations.com/)
- **Author:** Pedro Thomaz
- **Published:** 2026-05-16
- **Updated:** 2026-11-08
- **Language:** pt
- **Topics:** Cockpit, Headless CMS, PHP, Self-hosted, Production

---

Lançámos quatro sites em produção com Cockpit CMS v2 nos últimos 18 meses. Incluindo este. Não é o headless CMS mais pesquisado no Google — essa coroa pertence ao Sanity, Strapi, Contentful, por alguma ordem — mas o Cockpit encaixa num nicho que os nomes famosos ignoram: **workloads de conteúdo pequenas a médias em infraestrutura barata, editadas por pessoas que não querem uma sessão de formação de 90 minutos.**

Eis doze coisas que aprendemos à força. Se estás a avaliar Cockpit, isto poupa-te uma semana.

## 1. Cockpit v2 não é Cockpit v1

Se um tutorial é anterior a 2024, deita fora. A forma da API mudou. O layout de diretorias mudou. O admin UI mudou. O data model mudou. v2 é, para efeitos práticos, um produto diferente. Filtra os resultados de pesquisa por "v2" ou "2024+".

## 2. Self-hosting significa que és o dono do WAL

O storage default do Cockpit v2 é SQLite. SQLite usa Write-Ahead Logging por defeito. Os ficheiros `.sqlite-wal` e `.sqlite-shm` interessam. Se fizeres rsync só do ficheiro `.sqlite` para um servidor novo, perdes writes recentes. Ou fazes checkpoint primeiro (`PRAGMA wal_checkpoint(FULL)`) ou copias os três ficheiros juntos.

## 3. O cache de schema é real

Depois de editar um model JSON, o Cockpit faz cache do schema parseado em `storage/data/app.memory.sqlite`. Até apagares esse ficheiro, o admin UI mostra campos antigos. Mete isto no teu deploy script.

## 4. i18n é por campo, não por documento

Marcas *campos* como `i18n: true` no schema. O Cockpit guarda como `field` (locale default) e `field_pt`, `field_es`, etc. A API serve o locale pedido via `?locale=pt`. Planeia o schema com isto em mente; pôr i18n depois é trabalho manual de SQL.

## 5. A API key no URL é um tiro no pé

Não passes a API key como query parameter. Vai parar a logs, ao histórico do browser, a referrer headers. Usa o header HTTP `api-key`. Constrói o teu fetch helper para o forçar.

## 6. Assets de imagem precisam de proxy

A API de imagens do Cockpit exige a API key. Se referenciares URLs do Cockpit diretamente do frontend, expões a key. Escrevemos um proxy PHP de 30 linhas que aceita `?id=&w=`, faz fetch com a key no servidor, e devolve WebP com headers de cache. Isto é verdade para *qualquer* headless CMS com asset API privada.

## 7. Self-HTTPS loopback parte em alguns hosts

Quando PHP em `exemplo.com` tenta chamar `https://exemplo.com/cockpit/api/...`, alguns shared hosts recusam o loopback. `file_get_contents()` devolve false sem erro. Solução: usa cURL (funciona onde file_get_contents não funciona) *com fallback sensato*. Não assumas que o teu ambiente local é igual ao de produção.

## 8. Cockpit não tem webhook nativo para "conteúdo mudou"

Há um event system dentro do PHP do Cockpit, mas não há webhook outbound out-of-the-box em save. Se precisas de invalidar um CDN no publish, escreves a ponte: um pequeno addon do Cockpit que ouve `collection.save.after` e faz POST para o endpoint de purge do CDN.

## 9. O JSON do model é a tua source of truth

O UI do Cockpit deixa-te construir models visualmente, mas mete os ficheiros JSON do model em version control. Guarda-os no repo ao lado do código do site. Se a tua instância do Cockpit morrer, podes reconstruir o schema todo a partir desses ficheiros. Os dados em si vivem em SQLite — faz backup à parte.

## 10. Roles e API keys estão desacoplados

Uma API key tem uma role anexa à criação. Mudar a role de uma API key no admin UI obriga a apagar a key e criar uma nova. Não pendures API keys num deploy antes de decidir que permissões precisa.

## 11. Markdown não é o default — WYSIWYG é

Se queres devs a escrever em Markdown e editores a escrever em WYSIWYG, escolhes o widget WYSIWYG. O output é HTML. Embebe Prism.js ou similar no lado de rendering se quiseres code blocks com syntax highlighting; o Cockpit não faz isso por ti.

## 12. A comunidade é pequena mas responsiva

As GitHub issues do Cockpit recebem atenção direta do maintainer (Artur Heinze). A documentação é irregular — lê o source. É o preço de admissão de um CMS sem VC por trás: menos Stack Overflow, mais ler `modules/Content/Controller/Api.php` quando algo se comporta de forma estranha. Achamos isto mais fiável do que lidar com paid support em fornecedores com VC.

## As mensagens de erro, e o que querem mesmo dizer

Nenhuma destas produz um stack trace. Todas se parecem com outra coisa qualquer — é por isso que custam horas. Todas aconteceram neste site.

### Um modelo acabado de criar devolve 404 na API

Criaste `tender_source`, o admin mostra-o, e `/api/content/items/tender_source` responde 404. O modelo foi gravado com outro nome: o `Content/Helper/Model.php` corre `preg_replace('/[^A-Za-z0-9]/', '', $name)` ao criar, e `tender_source` ficou `tendersource`. Underscores e hífenes desaparecem em silêncio, sem aviso nem erro. Num nome de modelo, só letras e dígitos.

### createModel() e updateModel() devolvem false em vez de lançar

Uma gravação de modelo que falha não é uma exceção. Ambos devolvem `false`, por isso o `try/catch` não apanha nada e um importador que só vigie exceções anuncia sucesso sem ter criado nada. Verifica o valor devolvido e confirma a seguir com `exists()` — a fonte da verdade é o que está gravado, não o retorno.

### Um POST parcial criou um item duplicado em vez de atualizar

O `saveItem()` funde quando o payload leva `_id` — `array_merge($current, $item)` — por isso enviar dois campos atualiza dois campos e deixa o resto quieto. Sem `_id`, o mesmo payload funde com os valores por omissão do modelo e insere um item novo. Entre atualizar e duplicar vai uma chave.

### As traduções parecem ter desaparecido depois de gravar

Escrevem-se os campos i18n com chaves de sufixo no payload por omissão — `title_pt`, `body_es` — mas ler o item de volta sem `?locale=` devolve só a língua por omissão, sem nenhuma chave de sufixo na resposta. Não se perdeu nada; a leitura é que é localizada. Passa `?locale=pt` para as ver. E quando filtras ao mesmo tempo, o filtro tem de ser uma *string* JSON, não um array de query aninhado.

### Página em branco depois de mudar a versão de PHP

O Cockpit 2.14 exige PHP 8.3. Em qualquer versão anterior, o `bootstrap.php` chega ao `platform_check.php` do Composer, que chama `die()` — não é uma exceção. Nenhum `try/catch` a intercepta, por isso qualquer página que arranque o Cockpit sai em branco, sem nada no corpo da resposta. Se o teu alojamento tem uma versão de PHP para a web e outra para o cron, a tarefa agendada morre assim enquanto o site continua a funcionar.

### À procura de entries-batchedit.tag?

Então estás a ler documentação do Cockpit v1. Os ficheiros `.tag` do riot não existem no v2 — a edição em lote vive em `Content/views/collection/items.php`. A maior parte das respostas sobre Cockpit que há na web é anterior ao v2 e descreve outro produto; ver a lição 1.

## Perguntas frequentes

### Posso seguir tutoriais de Cockpit CMS anteriores a 2024 para a v2?

Não — o Cockpit CMS v2 não é a v1, por isso deve deitar fora os tutoriais anteriores a 2024. Muitos padrões e comportamentos mudaram entre as versões principais.

### Como faço uma cópia de segurança segura de uma base de dados SQLite do Cockpit auto-alojado?

Como o SQLite usa Write-Ahead Logging (WAL), copie os ficheiros `.sqlite`, `-wal` e `-shm` em conjunto, ou faça checkpoint primeiro. Auto-alojar significa que o WAL é seu, por isso copiar apenas o ficheiro principal arrisca uma cópia inconsistente.

### As minhas alterações de schema não aparecem depois de editar um modelo — porquê?

O Cockpit tem um cache de schema real, por isso apague `storage/data/app.memory.sqlite` depois de editar modelos. O JSON do modelo é a sua verdadeira fonte de verdade, por isso coloque-o sob controlo de versões.

### Como é que o Cockpit trata a internacionalização e como devo passar a chave de API?

A i18n é por campo, não por documento — trabalha com `field`, `field_pt` e `field_es`, pedindo uma localização com `?locale=pt`. Passe as credenciais no cabeçalho `api-key` em vez de no URL, o que é uma armadilha.

### Como purgo um CDN quando o conteúdo muda no Cockpit?

O Cockpit não tem webhook de saída integrado para eventos de conteúdo alterado, por isso escreva um addon em `collection.save.after` para purgar o CDN. Para imagens, use um proxy que mantenha a chave do lado do servidor e devolva WebP.

## Em resumo

Se precisas de um CMS que seja: gratuito, self-hostable em infra barata, rápido de editar, com i18n first-class e REST API a sério — o Cockpit é a melhor opção em 2026. Se precisas de edição colaborativa em tempo real, um exército de integrações out-of-the-box, ou documentação de mão dada — escolhe outro. O trade-off é honesto. Pagamo-lo com gosto.

---

Publicado por amplified creations, estúdio de tecnologia independente em Leiria e Lisboa, Portugal. Contacto: info@amplifiedcreations.com · Mais: https://amplifiedcreations.com/llms.txt
