Run Slack Apps Privately with Socket Mode and Bolt
Socket Mode lets Slack apps connect via outbound WebSocket, skipping public HTTPS endpoints while Bolt handles reconnection and event dispatch.
07 Oct 2026, 21:27 UTC

Problem: your Slack bot needs to run where public endpoints are not allowed
Many internal Slack apps run on laptops, private VMs, or serverless platforms that cannot accept inbound HTTPS requests. The classic Events API requires a public URL, a TLS certificate, and firewall rules just to receive app_mention or message.channels events. That infrastructure is overhead you do not need for a private integration.
Thesis: Socket Mode lets the app initiate the connection
Slack Socket Mode reverses the connection direction. Your app opens an outbound WebSocket to Slack, so Slack pushes events to you over that socket. The Bolt SDK manages the WebSocket lifecycle, automatic reconnection with exponential backoff, and dispatches Events API payloads as typed callbacks. You write handlers, not load balancers.
How the connection lifecycle works
When you start a Bolt app with Socket Mode enabled, the SDK calls apps.connections.open using an app-level token (an xapp-... string). Slack returns a wss:// URL. Bolt opens the WebSocket, performs the handshake, and begins receiving event payloads. If the network drops or Slack restarts the connection, Bolt reconnects automatically. Any in-memory state is lost during a reconnect, so handlers should treat the process as stateless.
Event handling and the three-second rule
Interactive components such as buttons, select menus, modals, and slash commands arrive over Socket Mode with a trigger_id and response_url. Your handler must call ack() within three seconds. If you miss the deadline, Slack shows a 'failed to load' error to the user. After acknowledging, you can reply with say(), open a view with client.views.open(), or respond asynchronously using respond(). For heavy work, acknowledge immediately and offload processing to a queue such as SQS, Bull, or Celery.
Worked example: a minimal mention bot
Run this on any machine with Node.js installed. No public port is exposed.
const { App } = require('@slack/bolt');
// Required environment variables:
// SLACK_BOT_TOKEN - xoxb-... bot token with chat:write
// SLACK_XAPP_TOKEN - xapp-A1B2C3... app-level token with connections:write
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
appToken: process.env.SLACK_XAPP_TOKEN,
socketMode: true,
});
app.event('app_mention', async ({ say, ack }) => {
await ack(); // acknowledge within 3 seconds
await say('Hello from Socket Mode!');
});
(async () => {
await app.start();
console.log('Socket Mode app is running');
})();
Install the dependency with npm install @slack/bolt. Generate the app-level token in the Slack API dashboard under your app's Socket Mode settings. Keep the token secret; it grants broad permissions across every workspace where the app is installed.
Trade-offs and limitations
- Throughput: one Socket Mode connection supports up to 30 events per second per workspace. Scale horizontally by running multiple instances with the same app-level token, but externalize state in Redis or a database.
- Interaction deadline:
ack()must complete within three seconds. Missing it surfaces an error to the user. - Stateless reconnects: WebSocket drops wipe in-memory caches and pending modals. Store transient data externally if you need it across reconnects.
- Payload size: events larger than 100 KB, such as some
file_shareevents, are dropped. Use thefiles.infoAPI to retrieve metadata instead. - Slack Connect: cross-workspace events in shared channels still require a public Events API endpoint.
- Serverless: many serverless runtimes terminate idle WebSocket connections after 30-60 seconds. Use a long-lived VM, container, or a WebSocket-compatible sidecar.
Verification checklist
- Create a Slack app in your workspace and enable Socket Mode in the app settings.
- Generate an app-level token with
connections:writeand your bot token withchat:write. Store both as environment variables. - Run
npm install @slack/boltand execute the example script above. - Mention the app in a channel. Confirm the handler logs appear in your console without using ngrok or a public URL.
- Open the Slack app dashboard, select your app, and check the Logs tab for WebSocket connect and event delivery entries.
- Kill the Node process to simulate a network partition. Restart it and confirm Bolt reconnects and resumes receiving events without manual reconfiguration.
Closing
Socket Mode removes the need for public HTTPS endpoints, TLS certificates, and inbound firewall rules, making it practical for private networks and internal tools. You still own connection health, the three-second acknowledgment window, and state externalization for scaling. Follow the checklist above, respect the limits, and you can run reliable Slack integrations without exposing a public endpoint.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.