diff --git a/services/backup/src/constants.rs b/services/backup/src/constants.rs
new file mode 100644
--- /dev/null
+++ b/services/backup/src/constants.rs
@@ -0,0 +1,3 @@
+pub const DEFAULT_GRPC_SERVER_PORT: u64 = 50051;
+pub const LOG_LEVEL_ENV_VAR: &str =
+  tracing_subscriber::filter::EnvFilter::DEFAULT_ENV;
diff --git a/services/backup/src/main.rs b/services/backup/src/main.rs
--- a/services/backup/src/main.rs
+++ b/services/backup/src/main.rs
@@ -1,5 +1,42 @@
+use anyhow::Result;
+use std::net::SocketAddr;
+use tonic::transport::Server;
+use tracing::{info, Level};
+use tracing_subscriber::EnvFilter;
+
+use crate::service::{BackupServiceServer, MyBackupService};
+
+pub mod constants;
 pub mod service;
 
-fn main() {
-  println!("Hello, world!");
+fn configure_logging() -> Result<()> {
+  let filter = EnvFilter::builder()
+    .with_default_directive(Level::INFO.into())
+    .with_env_var(constants::LOG_LEVEL_ENV_VAR)
+    .from_env_lossy();
+
+  let subscriber = tracing_subscriber::fmt().with_env_filter(filter).finish();
+  tracing::subscriber::set_global_default(subscriber)?;
+  Ok(())
+}
+
+async fn run_grpc_server() -> Result<()> {
+  let addr: SocketAddr =
+    format!("[::]:{}", constants::DEFAULT_GRPC_SERVER_PORT).parse()?;
+  let blob_service = MyBackupService::default();
+
+  info!("Starting gRPC server listening at {}", addr.to_string());
+  Server::builder()
+    .add_service(BackupServiceServer::new(blob_service))
+    .serve(addr)
+    .await?;
+
+  Ok(())
+}
+
+#[tokio::main]
+async fn main() -> Result<()> {
+  configure_logging()?;
+
+  run_grpc_server().await
 }
diff --git a/services/backup/src/service/mod.rs b/services/backup/src/service/mod.rs
--- a/services/backup/src/service/mod.rs
+++ b/services/backup/src/service/mod.rs
@@ -1,3 +1,73 @@
-pub mod proto {
+use proto::backup_service_server::BackupService;
+use std::pin::Pin;
+use tokio_stream::Stream;
+use tonic::{Request, Response, Status};
+use tracing::instrument;
+
+mod proto {
   tonic::include_proto!("backup");
 }
+pub use proto::backup_service_server::BackupServiceServer;
+
+#[derive(Default)]
+pub struct MyBackupService {}
+
+// gRPC implementation
+#[tonic::async_trait]
+impl BackupService for MyBackupService {
+  type CreateNewBackupStream = Pin<
+    Box<
+      dyn Stream<Item = Result<proto::CreateNewBackupResponse, Status>> + Send,
+    >,
+  >;
+
+  #[instrument(skip(self))]
+  async fn create_new_backup(
+    &self,
+    _request: Request<tonic::Streaming<proto::CreateNewBackupRequest>>,
+  ) -> Result<Response<Self::CreateNewBackupStream>, Status> {
+    Err(Status::unimplemented("unimplemented"))
+  }
+
+  #[instrument(skip(self))]
+  async fn send_log(
+    &self,
+    _request: Request<tonic::Streaming<proto::SendLogRequest>>,
+  ) -> Result<Response<proto::SendLogResponse>, Status> {
+    Err(Status::unimplemented("unimplemented"))
+  }
+
+  type RecoverBackupKeyStream = Pin<
+    Box<
+      dyn Stream<Item = Result<proto::RecoverBackupKeyResponse, Status>> + Send,
+    >,
+  >;
+
+  #[instrument(skip(self))]
+  async fn recover_backup_key(
+    &self,
+    _request: Request<tonic::Streaming<proto::RecoverBackupKeyRequest>>,
+  ) -> Result<Response<Self::RecoverBackupKeyStream>, Status> {
+    Err(Status::unimplemented("unimplemented"))
+  }
+
+  type PullBackupStream = Pin<
+    Box<dyn Stream<Item = Result<proto::PullBackupResponse, Status>> + Send>,
+  >;
+
+  #[instrument(skip(self))]
+  async fn pull_backup(
+    &self,
+    _request: Request<proto::PullBackupRequest>,
+  ) -> Result<Response<Self::PullBackupStream>, Status> {
+    Err(Status::unimplemented("unimplemented"))
+  }
+
+  #[instrument(skip(self))]
+  async fn add_attachments(
+    &self,
+    _request: Request<proto::AddAttachmentsRequest>,
+  ) -> Result<Response<()>, Status> {
+    Err(Status::unimplemented("unimplemented"))
+  }
+}