Getting 404 on own service but dashboard and whoami is loading fine (Traefik on Azure Kubernetes)

Hi,
I have been trying to setup Traefik as ingress controller on our kubernetes cluster hosted in Azure which also has Istio Ingress (for which our services are exposed fine).
Latest traefik version was installed on our cluster via helm v3

helm upgrade --install traefik traefik/traefik --n portals -f values.yaml

Here is the values file:

 # Default values for Traefik
    image:
      name: traefik
      tag: 2.2.8
      pullPolicy: IfNotPresent
    
    #
    # Configure the deployment
    #
    deployment:
      enabled: true
      # Number of pods of the deployment
      replicas: 1
      # Additional deployment annotations (e.g. for jaeger-operator sidecar injection)
      annotations: {}
      # Additional pod annotations (e.g. for mesh injection or prometheus scraping)
      podAnnotations: {}
      # Additional containers (e.g. for metric offloading sidecars)
      additionalContainers: []
      # Additional initContainers (e.g. for setting file permission as shown below)
      initContainers: []
        # The "volume-permissions" init container is required if you run into permission issues.
        # Related issue: https://github.com/containous/traefik/issues/6972
        # - name: volume-permissions
        #   image: busybox:1.31.1
        #   command: ["sh", "-c", "chmod -Rv 600 /data/*"]
        #   volumeMounts:
        #     - name: data
        #       mountPath: /data
      # Custom pod DNS policy. Apply if `hostNetwork: true`
      # dnsPolicy: ClusterFirstWithHostNet
    
    # Pod disruption budget
    podDisruptionBudget:
      enabled: false
      # maxUnavailable: 1
      # minAvailable: 0
    
    # Create an IngressRoute for the dashboard
    ingressRoute:
      dashboard:
        enabled: true
        # Additional ingressRoute annotations (e.g. for kubernetes.io/ingress.class)
        annotations: {}
        # Additional ingressRoute labels (e.g. for filtering IngressRoute by custom labels)
        labels: {}
    
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
    
    
    #
    # Configure providers
    #
    providers:
      kubernetesCRD:
        enabled: true
      kubernetesIngress:
        enabled: true
        # IP used for Kubernetes Ingress endpoints
        publishedService:
          enabled: false
          # Published Kubernetes Service to copy status from. Format: namespace/servicename
          # By default this Traefik service
          # pathOverride: ""
    
    #
    # Add volumes to the traefik pod.
    # This can be used to mount a cert pair or a configmap that holds a config.toml file.
    # After the volume has been mounted, add the configs into traefik by using the `additionalArguments` list below, eg:
    # additionalArguments:
    # - "--providers.file.filename=/config/dynamic.toml"
    volumes: []
    # - name: public-cert
    #   mountPath: "/certs"
    #   type: secret
    # - name: configs
    #   mountPath: "/config"
    #   type: configMap
    
    # Logs
    # https://docs.traefik.io/observability/logs/
    logs:
      # Traefik logs concern everything that happens to Traefik itself (startup, configuration, events, shutdown, and so on).
      general:
        # By default, the logs use a text format (common), but you can
        # also ask for the json format in the format option
        # format: json
        # By default, the level is set to ERROR. Alternative logging levels are DEBUG, PANIC, FATAL, ERROR, WARN, and INFO.
        level: INFO
      access:
        # To enable access logs
        enabled: true
        # By default, logs are written using the Common Log Format (CLF).
        # To write logs in JSON, use json in the format option.
        # If the given format is unsupported, the default (CLF) is used instead.
        # format: json
        # To write the logs in an asynchronous fashion, specify a bufferingSize option.
        # This option represents the number of log lines Traefik will keep in memory before writing
        # them to the selected output. In some cases, this option can greatly help performances.
        # bufferingSize: 100
        # Filtering https://docs.traefik.io/observability/access-logs/#filtering
        filters: {}
          # statuscodes: "200,300-302"
          # retryattempts: true
          # minduration: 10ms
        # Fields
        # https://docs.traefik.io/observability/access-logs/#limiting-the-fieldsincluding-headers
        fields:
          general:
            defaultmode: keep
            names: {}
              # Examples:
              # ClientUsername: drop
          headers:
            defaultmode: drop
            names: {}
              # Examples:
              # User-Agent: redact
              # Authorization: drop
              # Content-Type: keep
    
    globalArguments:
      - "--global.checknewversion"
      - "--global.sendanonymoususage"
    
    #
    # Configure Traefik static configuration
    # Additional arguments to be passed at Traefik's binary
    # All available options available on https://docs.traefik.io/reference/static-configuration/cli/
    ## Use curly braces to pass values: `helm install --set="additionalArguments={--providers.kubernetesingress.ingressclass=traefik-internal,--log.level=DEBUG}"`
    additionalArguments: []
    #  - "--providers.kubernetesingress.ingressclass=traefik-internal"
    #  - "--log.level=DEBUG"
    
    # Environment variables to be passed to Traefik's binary
    env: []
    # - name: SOME_VAR
    #   value: some-var-value
    # - name: SOME_VAR_FROM_CONFIG_MAP
    #   valueFrom:
    #     configMapRef:
    #       name: configmap-name
    #       key: config-key
    # - name: SOME_SECRET
    #   valueFrom:
    #     secretKeyRef:
    #       name: secret-name
    #       key: secret-key
    
    envFrom: []
    # - configMapRef:
    #     name: config-map-name
    # - secretRef:
    #     name: secret-name
    
    # Configure ports
    ports:
      # The name of this one can't be changed as it is used for the readiness and
      # liveness probes, but you can adjust its config to your liking
      traefik:
        port: 9000
        # Use hostPort if set.
        # hostPort: 9000
        #
        # Use hostIP if set. If not set, Kubernetes will default to 0.0.0.0, which
        # means it's listening on all your interfaces and all your IPs. You may want
        # to set this value if you need traefik to listen on specific interface
        # only.
        # hostIP: 192.168.100.10
    
        # Defines whether the port is exposed if service.type is LoadBalancer or
        # NodePort.
        #
        # You SHOULD NOT expose the traefik port on production deployments.
        # If you want to access it from outside of your cluster,
        # use `kubectl proxy` or create a secure ingress
        expose: false
        # The exposed port for this service
        exposedPort: 9000
        # The port protocol (TCP/UDP)
        protocol: TCP
      web:
        port: 8000
        # hostPort: 8000
        expose: true
        exposedPort: 80
        # The port protocol (TCP/UDP)
        protocol: TCP
        # Use nodeport if set. This is useful if you have configured Traefik in a
        # LoadBalancer
        # nodePort: 32080
        # Port Redirections
        # Added in 2.2, you can make permanent redirects via entrypoints.
        # https://docs.traefik.io/routing/entrypoints/#redirection
        # redirectTo: websecure
      websecure:
        port: 8443
        # hostPort: 8443
        expose: true
        exposedPort: 443
        # The port protocol (TCP/UDP)
        protocol: TCP
        # nodePort: 32443
    
    # Options for the main traefik service, where the entrypoints traffic comes
    # from.
    service:
      enabled: true
      type: LoadBalancer
      # Additional annotations (e.g. for cloud provider specific config)
      annotations: {}
      # Additional entries here will be added to the service spec. Cannot contains
      # type, selector or ports entries.
      spec: {}
        # externalTrafficPolicy: Cluster
        # loadBalancerIP: "1.2.3.4"
        # clusterIP: "2.3.4.5"
      loadBalancerSourceRanges: []
        # - 192.168.0.1/32
        # - 172.16.0.0/16
      externalIPs: []
        # - 1.2.3.4
    
    ## Create HorizontalPodAutoscaler object.
    ##
    autoscaling:
      enabled: false
    #   minReplicas: 1
    #   maxReplicas: 10
    #   metrics:
    #   - type: Resource
    #     resource:
    #       name: cpu
    #       targetAverageUtilization: 60
    #   - type: Resource
    #     resource:
    #       name: memory
    #       targetAverageUtilization: 60
    
    # Enable persistence using Persistent Volume Claims
    # ref: http://kubernetes.io/docs/user-guide/persistent-volumes/
    # After the pvc has been mounted, add the configs into traefik by using the `additionalArguments` list below, eg:
    # additionalArguments:
    # - "--certificatesresolvers.le.acme.storage=/data/acme.json"
    # It will persist TLS certificates.
    persistence:
      enabled: false
    #  existingClaim: ""
      accessMode: ReadWriteOnce
      size: 128Mi
      # storageClass: ""
      path: /data
      annotations: {}
      # subPath: "" # only mount a subpath of the Volume into the pod
    
    # If hostNetwork is true, runs traefik in the host network namespace
    # To prevent unschedulabel pods due to port collisions, if hostNetwork=true
    # and replicas>1, a pod anti-affinity is recommended and will be set if the
    # affinity is left as default.
    hostNetwork: false
    
    # Whether Role Based Access Control objects like roles and rolebindings should be created
    rbac:
      enabled: true
    
      # If set to false, installs ClusterRole and ClusterRoleBinding so Traefik can be used across namespaces.
      # If set to true, installs namespace-specific Role and RoleBinding and requires provider configuration be set to that same namespace
      namespaced: false
    
    # The service account the pods will use to interact with the Kubernetes API
    serviceAccount:
      # If set, an existing service account is used
      # If not set, a service account is created automatically using the fullname template
      name: ""
    
    # Additional serviceAccount annotations (e.g. for oidc authentication)
    serviceAccountAnnotations: {}
    
    resources: {}
      # requests:
      #   cpu: "100m"
      #   memory: "50Mi"
      # limits:
      #   cpu: "300m"
      #   memory: "150Mi"
    affinity: {}
    # # This example pod anti-affinity forces the scheduler to put traefik pods
    # # on nodes where no other traefik pods are scheduled.
    # # It should be used when hostNetwork: true to prevent port conflicts
    #   podAntiAffinity:
    #     requiredDuringSchedulingIgnoredDuringExecution:
    #     - labelSelector:
    #         matchExpressions:
    #         - key: app
    #           operator: In
    #           values:
    #           - {{ template "traefik.name" . }}
    #       topologyKey: failure-domain.beta.kubernetes.io/zone
    nodeSelector: {}
    tolerations: []
    
    # Pods can have priority.
    # Priority indicates the importance of a Pod relative to other Pods.
    priorityClassName: ""
    
    # Set the container security context
    # To run the container with ports below 1024 this will need to be adjust to run as root
    securityContext:
      capabilities:
        drop: [ALL]
      readOnlyRootFilesystem: true
      runAsGroup: 65532
      runAsNonRoot: true
      runAsUser: 65532
    
    podSecurityContext:
      fsGroup: 65532

Pod is running fine with the external IP.
I also created an ingressroute for the dashboard and can see it online by adding the hostname in my local hosts file.
I then tried deploying whoami service and created an ingress for it, added example.com in my local hosts file and getting something when I navigate to example.com in my browser.
However, when I added an ingress to route to my own service, I get a 404.

* Rebuilt URL to: http://{my domain}/
*   Trying {external ip}...
* TCP_NODELAY set
* Connected to {my domain} ({external ip}) port 80 (#0)
> GET / HTTP/1.1
> Host: {my domain}
> User-Agent: curl/7.55.1
> Accept: */*
>
< HTTP/1.1 404 Not Found
< content-type: text/plain; charset=utf-8
< x-content-type-options: nosniff
< date: Fri, 15 Jan 2021 05:26:23 GMT
< content-length: 19
< x-envoy-upstream-service-time: 0
< server: istio-envoy
< x-envoy-decorator-operation: traefik.portals.svc.cluster.local:80/*
<
404 page not found
* Connection #0 to host {my domain} left intact

Based on the traefik dashboard, router to my service is successful and status of the pods are good (pointing to correct urls) when I check its service details

Here is the yaml file I used to create the ingress

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: staffportalv2-ingress
  annotations:
    kubernetes.io/ingress.class: traefik
spec:
  rules:
    - host: staffportalv2.axitesting.com
      http:
        paths:
          - path: /
            backend:
              serviceName: portals              
              servicePort: http

Here is the service yaml just for reference as the service and deployment has been deployed before and is also referenced by istio ingress.

apiVersion: v1
kind: Service
metadata:
  name: portals 
spec:
  type: ClusterIP
  ports:
    - port: 8080
      targetPort: 80
      protocol: TCP
      name: http
  selector:
    app: portals

The service pods for portals are confirmed to be running as I can browse it properly using the exposed IP of istio ingress.
Will really appreciate your help in figuring out what is wrong or missing in my setup. Thank you!