Shiny Server: mais tempo de conexão e a tela de reconexão de volta
Ajustando timeouts e a reconexão de sessão em painéis Shiny no Debian
By Rodrigo Borges in Infraestrutura R Shiny
October 14, 2026
Software aberto, do servidor ao painel
Aqui na Distintive, e em projetos parceiros, trabalhamos com software livre de ponta a ponta: os dados são tratados em R, os painéis são aplicações Shiny, a infraestrutura roda em servidores Debian com o Shiny Server e até o site que você lê é escrito em R Markdown e publicado com Hugo via blogdown. Dois exemplos públicos dessa abordagem são o painel de Desenvolvimento Regional do Capital Data Lab e o painel do Coletivo Vira Voto, ambos com mapas, séries e indicadores construídos inteiramente com esse ecossistema.
Como qualquer painel Shiny de uso público, eles esbarram num problema recorrente de usabilidade: quando a conexão do usuário oscila (celular na rua, wifi instável, notebook que suspende), a sessão é derrubada rápido demais e a pessoa vê uma tela congelada, sem nenhuma pista de que bastava reconectar. Este artigo mostra como ajustamos o Shiny Server para aumentar o tempo antes de considerar a conexão perdida e para exibir a tela de reconexão de volta.
Resumo rápido
Duas mudanças resolvem o problema: uma no servidor, outra na aplicação.
1) No arquivo de configuração do servidor (/etc/shiny-server/default.config):
sockjs_heartbeat_delay 10;
sockjs_disconnect_delay 30;
disable_websockets true;
http_keepalive_timeout 120;
app_init_timeout 120;
app_idle_timeout 3600;
reconnect true;
2) No início da função server() da aplicação:
server <- function(input, output, session) {
session$allowReconnect("force")
# ... resto do código
}
As diretivas
sockjs_*ehttp_keepalive_timeoutaumentam o tempo até a conexão ser considerada perdida. Osession$allowReconnect("force")obriga o navegador a mostrar a tela de reconexão (com “Try now” e a opção de recarregar) mesmo quando o transporte não oferece reconexão automaticamente.
1. Diagnóstico: onde está cada coisa
Antes de editar, confirme o caminho da config, o usuário de execução e a pasta da aplicação:
# Arquivos do Shiny Server
ls -l /etc/shiny-server/
# Qual arquivo é realmente lido (shiny-server.conf costuma ser um symlink)
readlink -f /etc/shiny-server/shiny-server.conf
# Referência com TODAS as diretivas aceitas
cat /etc/shiny-server/shiny-server-rules.config
# Usuário e pasta servidos
grep -nE "run_as|listen|site_dir|app_dir|location" /etc/shiny-server/default.config
# Versão instalada (a sintaxe muda entre versões)
/usr/bin/shiny-server --version
Cuidado: rodar shiny-server sem argumentos inicia um servidor (ele não tem
--help). Se a porta já estiver em uso, ele falha com EADDRINUSE e encerra,
mas o ideal é não usar esse comando como teste em produção.
2. Ajustando o timeout no Shiny Server
O diretório /etc/shiny-server/ pertence ao root, então o backup e a edição
exigem privilégios de administrador:
sudo cp -a /etc/shiny-server/default.config \
/etc/shiny-server/default.config.bak.$(date +%Y%m%d-%H%M%S)
sudo nano /etc/shiny-server/default.config
O arquivo final deve ficar parecido com isto (ajuste run_as, listen e
site_dir/app_dir conforme o seu servidor):
# Roda as aplicações como este usuário
run_as shiny;
# --- timeouts globais de conexão / sessão -------------------------------
sockjs_heartbeat_delay 10; # heartbeat mais frequente (default 25s)
sockjs_disconnect_delay 30; # espera mais antes de considerar desconectado (default 5s)
disable_websockets true; # necessário para o valor acima valer (WebSocket limita a ~10s)
http_keepalive_timeout 120; # mantém keep-alive HTTP aberto (default 45s)
app_init_timeout 120; # tempo para o processo R iniciar (default 60s)
app_idle_timeout 3600; # mantém o processo R vivo sem conexões (default 5s)
reconnect true; # oferece reconexão de sessão (default true)
# --- servidor HTTP ------------------------------------------------------
server {
listen 3838;
location / {
site_dir /srv/shiny-server;
log_dir /var/log/shiny-server;
directory_index off;
}
}
As diretivas sockjs_*, http_keepalive_timeout, app_*, reconnect e
disable_websockets podem ficar no topo do arquivo (escopo global $) ou dentro
de server/location. No topo, valem para todas as aplicações.
3. O que cada diretiva faz
| Diretiva | Padrão | Sugerido | Efeito |
|---|---|---|---|
sockjs_heartbeat_delay |
25s | 10s | Envia heartbeat com mais frequência, evitando que proxies, NAT ou máquinas que hibernam derrubem conexões ociosas. |
sockjs_disconnect_delay |
5s | 30s | Tempo que o servidor espera por tráfego antes de tratar o cliente como desconectado. |
disable_websockets |
off | true |
O WebSocket tem limite fixo de ~10s. Desabilitar permite usar um disconnect maior; o transporte cai para xhr/eventsource streaming. |
http_keepalive_timeout |
45s | 120s | Mantém conexões HTTP keep-alive abertas por mais tempo entre requisições. |
app_init_timeout |
60s | 120s | Tempo máximo para o processo R iniciar antes de desistir. |
app_idle_timeout |
5s | 3600s | Mantém o processo R vivo sem conexões. Evita cold start quando o usuário recarrega ou reconecta. |
reconnect |
true | true | Mantém a oferta de reconexão à sessão existente após uma queda. |
4. Habilitando a tela de reconexão na aplicação
No arquivo server.R (ou app.R) da aplicação, adicione
session$allowReconnect("force") como a primeira linha da função server:
server <- function(input, output, session) {
session$allowReconnect("force")
session$onSessionEnded(stopApp) # linha já existente na maioria dos apps
# ... resto do código
}
Faça um backup e edite. Se o arquivo for somente leitura, copie para a mesma pasta (basta ter permissão de escrita no diretório) e substitua:
APP=/srv/shiny-server/minhaapp
# backup
cp -a "$APP/server.R" "$APP/server.R.orig"
# insere a linha de forma idempotente, apenas se ainda não existir
if ! grep -q 'allowReconnect("force")' "$APP/server.R"; then
awk '
{ print }
/^server *<- *function\(input, *output, *session\)\{/ && !done {
print " session$allowReconnect(\"force\")"
done = 1
}
' "$APP/server.R" > "$APP/.server.R.new" \
&& mv -f "$APP/.server.R.new" "$APP/server.R"
fi
Os valores aceitos por allowReconnect são TRUE, FALSE ou "force". Use
"force" para que o diálogo apareça sempre, inclusive quando o transporte
(polling, por exemplo) não anunciaria reconexão sozinho.
Atenção especial a apps que usam session$onSessionEnded(stopApp): uma queda
breve não encerra a sessão, pois a reconexão a mantém viva. Quando a sessão
termina de fato, o app é encerrado e reiniciado na próxima requisição, que é o
comportamento normal nesse padrão.
5. Aplicar e reiniciar
O server.R só é relido quando o processo R da aplicação reinicia. Reinicie o
serviço:
sudo systemctl restart shiny-server
Se o servidor não usa systemd, reinicie do jeito da sua distribuição. O
ExecReload do unit padrão usa kill -HUP, o que recarrega a configuração.
6. Verificando
# 1) o serviço subiu sem erro de parse na config
tail -n 30 /var/log/shiny-server.log
# 2) o status do serviço
systemctl status shiny-server
# 3) a linha foi aplicada
grep -n allowReconnect /srv/shiny-server/minhaapp/server.R
# 4) backup criado
ls -t /etc/shiny-server/default.config.bak.* | head -1
Depois, teste no navegador:
- Abra a aplicação e interaja um pouco.
- Desligue a rede da sua máquina (ou suspenda por 10 a 20 segundos) e religue.
- Deve aparecer a faixa “Disconnected” e o diálogo “Attempting to reconnect…” com “Try now”.
- Ao reconectar dentro da janela, a sessão continua onde estava; se expirar, a página é recarregada automaticamente.
7. Limites e armadilhas
- A janela de reconexão é fixa (~15s). O Shiny Server mantém a sessão viva
por cerca de 15 segundos após a desconexão, e isso não é configurável por
diretiva. Por isso o ganho real vem de
sockjs_heartbeat_delayeapp_idle_timeout: evitar a queda em primeiro lugar. - Proxy reverso na frente. Se houver Nginx, Apache ou HAProxy, aumente também
o timeout dele (no Nginx,
proxy_read_timeouteproxy_send_timeout) para um valor maior quesockjs_heartbeat_delay. disable_websockets trueaumenta a carga. Sem WebSocket, o app passa a usar streaming/polling, o que é mais tolerante a redes ruins, porém gera mais requisições. Com poucos usuários simultâneos o impacto é baixo. Para voltar atrás, basta comentar a linha e reduzirsockjs_disconnect_delaypara 10s.app_idle_timeoutalto consome memória. Manter processos R ociosos vivos usa RAM. Se o servidor hospeda muitas aplicações, use um valor intermediário (por exemplo 600s) em vez de 3600s.- Sintaxe com ponto e vírgula. Cada diretiva termina em
;. Um;faltando impede o servidor de subir, então confira o log logo após o restart.
8. Rollback
# Config
sudo cp -a /etc/shiny-server/default.config.bak.<TIMESTAMP> \
/etc/shiny-server/default.config
sudo systemctl restart shiny-server
# Aplicação
cp /srv/shiny-server/minhaapp/server.R.orig /srv/shiny-server/minhaapp/.server.R.new \
&& mv -f /srv/shiny-server/minhaapp/.server.R.new /srv/shiny-server/minhaapp/server.R
O ideal é aplicar primeiro em um servidor de teste, comparar o
/var/log/shiny-server.log antes e depois, e só então replicar nos demais.
Os ajustes foram validados em Shiny Server 1.5.20 e Shiny 1.11.1. Ajuste caminhos, usuário e porta conforme o ambiente.