Repository: basecamp/kamal (Ruby)

Issue:
Allow overriding custom `kamal` network
hey 👋🏻 

I'm running a couple of accessories that require host network mode and since v2, Kamnal forces the usage of custom `kamal` network.

Is there any chance to make this option configurable? These containers do not run behind a proxy anyway so there's no need for them to be discoverable


Discussion:
According to the upgrade guide, the recommendation for customizing the network is to use the `docker-setup` hook. https://kamal-deploy.org/docs/upgrading/network-changes/
then I would need to create a network called “kamal” with host access, which is far from ideal. i dont want all containers with host access, nor have to deal with port mapping because some containers port clash and host network exposes it all
@djmb I can add a configuration option to change the network if you are okay with this.
> then I would need to create a network called “kamal” with host access, which is far from ideal. i dont want all containers with host access, nor have to deal with port mapping because some containers port clash and host network exposes it all

I don't think this would work - it would put the app and proxy containers in a host network as well, and then the proxy won't be able to access the app container by its container ID.

@igor-alexandrov - yes please if you have a chance to add this 👍

We'd add something like `network: <name>` I guess and then you can set it to `network: host`?

```yaml
accessories:
  redis:
    image: redis:latest
    port: 6379
    network: host
```

you can have your accessories on multiple networks, for example for a Typesense cluster, static IPs are required. I solved this by adding:
```
accessories:
  typesense:
    options:
      network: name=my-custom-net,ip=my-custom-ip
```

this way the accessory is on the kamal network as well on my-custom-net
`host` network can't be mixed with `bridge` network for example
I have the same problem - node_explorer for monitoring server require network host. 

When setup accessory 

```
  node_explorer: 
    image: quay.io/prometheus/node-exporter:latest
    host: <SERVER.IP> 
    cmd: "--path.rootfs=/host --collector.systemd --collector.processes"
    directories:
      - /:/host:ro,rslave
    options: 
      net: "host"
      pid: "host"
```

It will not work because kamal force to use own network 

```
docker stderr: docker: conflicting options: cannot attach both user-defined and non-user-defined network-modes.
```

1) I dont think you want to manage monitoring/ops stuff with kamal, kamal should be used for stuff thats application related, use different tools to manage obsevability and other tasks (like ansible)
2) are you absolutely sure that node-explorer needs to run on host network?
#1020 this doesn't work :

```
INFO [797f1108] Running docker run --name my-app --detach --restart unless-stopped --network kamal --network "host" --cap-add "NET_ADMIN *******"
Releasing the deploy lock...
docker stderr: docker: conflicting options: cannot attach both user-defined and non-user-defined network-modes
Run 'docker run --help' for more information
```

now it's passing `--network kamal --network host` instead of overriding and only keeping `--network host`.
I don't think Kamal supports running container on `host` network, as you noted adding `options.network` will add aditional networks on top of the `kamal` network
@honzasterba while most accessories, it's fine to run on kamal network, it's just nice to also be able to run certain containers on the hostnetwork. For example, I had the following with Kamal v1 work fine.

```yaml
  accessories:
    newrelic-infra:
    image: newrelic/infrastructure:latest
    hosts:
      - web.host
      - db.host
      - redis.host
    env:
      secret:
        - NRIA_LICENSE_KEY
    volumes:
      - /:/host:ro
      - /var/run/docker.sock:/var/run/docker.sock
      - ...
    options:
      network: host
      cap-add: SYS_PTRACE
      pid: host
      cgroupns: host
```

This is no longer working. Question is, why not allow network to be overridden?
No idea. I guess you will have to try and implement it in a PR.



Patch:
diff --git a/lib/kamal/commands/accessory.rb b/lib/kamal/commands/accessory.rb
index 0c1b9009c..9abb6dfc0 100644
--- a/lib/kamal/commands/accessory.rb
+++ b/lib/kamal/commands/accessory.rb
@@ -1,7 +1,7 @@
 class Kamal::Commands::Accessory < Kamal::Commands::Base
   attr_reader :accessory_config
   delegate :service_name, :image, :hosts, :port, :files, :directories, :cmd,
-           :publish_args, :env_args, :volume_args, :label_args, :option_args,
+           :network_args, :publish_args, :env_args, :volume_args, :label_args, :option_args,
            :secrets_io, :secrets_path, :env_directory,
            to: :accessory_config
 
@@ -15,7 +15,7 @@ def run
       "--name", service_name,
       "--detach",
       "--restart", "unless-stopped",
-      "--network", "kamal",
+      *network_args,
       *config.logging_args,
       *publish_args,
       *env_args,
@@ -64,7 +64,7 @@ def execute_in_new_container(*command, interactive: false)
     docker :run,
       ("-it" if interactive),
       "--rm",
-      "--network", "kamal",
+      *network_args,
       *env_args,
       *volume_args,
       image,
diff --git a/lib/kamal/configuration/accessory.rb b/lib/kamal/configuration/accessory.rb
index 804a15029..3258c9d09 100644
--- a/lib/kamal/configuration/accessory.rb
+++ b/lib/kamal/configuration/accessory.rb
@@ -1,6 +1,8 @@
 class Kamal::Configuration::Accessory
   include Kamal::Configuration::Validation
 
+  DEFAULT_NETWORK = "kamal"
+
   delegate :argumentize, :optionize, to: Kamal::Utils
 
   attr_reader :name, :accessory_config, :env
@@ -38,6 +40,10 @@ def port
     end
   end
 
+  def network_args
+    argumentize "--network", network
+  end
+
   def publish_args
     argumentize "--publish", port if port
   end
@@ -173,4 +179,8 @@ def hosts_from_roles
         accessory_config["roles"].flat_map { |role| config.role(role).hosts }
       end
     end
+
+    def network
+      accessory_config["network"] || DEFAULT_NETWORK
+    end
 end
diff --git a/lib/kamal/configuration/docs/accessory.yml b/lib/kamal/configuration/docs/accessory.yml
index 86d49b77a..e77bf754f 100644
--- a/lib/kamal/configuration/docs/accessory.yml
+++ b/lib/kamal/configuration/docs/accessory.yml
@@ -90,3 +90,11 @@ accessories:
     # They are not created or copied before mounting:
     volumes:
       - /path/to/mysql-logs:/var/log/mysql
+
+    # Network
+    #
+    # The network the accessory will be attached to.
+    #
+    # Defaults to kamal:
+    network: custom
+


Test patch:
diff --git a/test/commands/accessory_test.rb b/test/commands/accessory_test.rb
index f3d71ffd3..1befd9e64 100644
--- a/test/commands/accessory_test.rb
+++ b/test/commands/accessory_test.rb
@@ -71,6 +71,14 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
       new_command(:busybox).run.join(" ")
   end
 
+  test "run in custom network" do
+    @config[:accessories]["mysql"]["network"] = "custom"
+
+    assert_equal \
+      "docker run --name app-mysql --detach --restart unless-stopped --network custom --log-opt max-size=\"10m\" --publish 3306:3306 --env MYSQL_ROOT_HOST=\"%\" --env-file .kamal/apps/app/env/accessories/mysql.env --label service=\"app-mysql\" private.registry/mysql:8.0",
+      new_command(:mysql).run.join(" ")
+  end
+
   test "start" do
     assert_equal \
       "docker container start app-mysql",
diff --git a/test/configuration/accessory_test.rb b/test/configuration/accessory_test.rb
index 2615dab60..f52209026 100644
--- a/test/configuration/accessory_test.rb
+++ b/test/configuration/accessory_test.rb
@@ -152,4 +152,13 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase
   test "options" do
     assert_equal [ "--cpus", "\"4\"", "--memory", "\"2GB\"" ], @config.accessory(:redis).option_args
   end
+
+  test "network_args default" do
+    assert_equal [ "--network", "kamal" ], @config.accessory(:mysql).network_args
+  end
+
+  test "network_args with configured options" do
+    @deploy[:accessories]["mysql"]["network"] = "database"
+    assert_equal [ "--network", "database" ], @config.accessory(:mysql).network_args
+  end
 end