3. Persistence
What it is
Persistence means the messages inside the queue are saved to disk, not just memory.
- Without persistence: If RabbitMQ crashes, all in-memory messages are lost.
- With persistence: Messages are stored on disk and survive a restart.
Example
Goal: Ensure messages survive a broker restart by combining durable queue + persistent messages.
// persistence_example.go package main import ( "log" amqp "github.com/rabbitmq/amqp091-go" ) func mustConn() *amqp.Connection { conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/") if err != nil { log.Fatal(err) } return conn } func main() { conn := mustConn() defer conn.Close() ch, err := conn.Channel() if err != nil { log.Fatal(err) } defer ch.Close() // Durable queue q, err := ch.QueueDeclare( "orders_queue_persist", true, // durable false, // autoDelete false, // exclusive false, // noWait nil, ) if err != nil { log.Fatal(err) } // Publish a persistent message if err := ch.Publish( "", q.Name, false, false, amqp.Publishing{ DeliveryMode: amqp.Persistent, // <--- persisted to disk ContentType: "text/plain", Body: []byte("Order #999 created"), }, ); err != nil { log.Fatal(err) } log.Println("persistent message published to a durable queue (message survives restarts)") }
Optional: Publisher Confirms (extra safety for producers)
If you also want the producer to know the broker safely received the message:
// publisher_confirms.go package main import ( "log" amqp "github.com/rabbitmq/amqp091-go" ) func mustConn() *amqp.Connection { conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/") if err != nil { log.Fatal(err) } return conn } func main() { conn := mustConn() defer conn.Close() ch, err := conn.Channel() if err != nil { log.Fatal(err) } defer ch.Close() if err := ch.Confirm(false); err != nil { log.Fatal(err) } acks := ch.NotifyPublish(make(chan amqp.Confirmation, 1)) q, _ := ch.QueueDeclare("orders_queue_confirm", true, false, false, false, nil) if err := ch.Publish("", q.Name, false, false, amqp.Publishing{ DeliveryMode: amqp.Persistent, Body: []byte("Order with confirm"), }); err != nil { log.Fatal(err) } confirm := <-acks if !confirm.Ack { log.Fatal("publish NOT confirmed by broker") } log.Println("publish confirmed by broker") }
Notes
- Install the driver:
go get github.com/rabbitmq/amqp091-go
- Run RabbitMQ locally (e.g., Docker):
docker run -p 5672:5672 -p 15672:15672 rabbitmq:3-management
- Combine durable queues + persistent messages + manual ACKs for end-to-end reliability.
How to enable
- Mark messages as persistent when publishing.
- Example in code (pseudocode):
channel.publish("exchange", "routing_key", Buffer.from("msg"), { persistent: true });
4. How They Work Together
For full reliability, you need all three:
- Durable queues – so the queue is still there after restart.
- Persistent messages – so messages inside the queue survive restart.
- Acknowledgments – so RabbitMQ only removes messages after the consumer confirms.
Note: Even with persistence, there is a small chance of data loss if RabbitMQ crashes before flushing to disk. For very critical systems, you may also use Publisher Confirms (RabbitMQ feature that tells the producer when a message is safely stored).
5. Best Practices
- Use manual ACKs to avoid losing messages during crashes.
- Make queues durable if they handle important data.
- Mark important messages as persistent.
- Combine with Publisher Confirms for end-to-end safety.
- Monitor queues to avoid overload (too many persistent messages can slow down the system).
Conclusion
Reliability is a key strength of RabbitMQ. By using acknowledgments, durability, and persistence, you can make sure messages are delivered safely, even if consumers fail or servers restart. These features make RabbitMQ a trusted backbone for critical applications such as payments, orders, and IoT systems.